Skip to content

Introduction to Ki: Part 2

tingham edited this page Mar 10, 2011 · 4 revisions

The first part to introducing Ki using states A, B, C and D might be a little too abstract, so instead let's work with an example based on a common scenario that everyone is familiar with: An application where the user can log in and log out of the app.

We want our application to have two primary states: when the user is logged out and when the user is logged in. Let's call those states loggedOut and loggedIn. The user can only be in one of those two states at a time; not both. When the user is in the logged out state a login pane is visible that allows the user to enter credentials and click a login button. When the user has logged in successfully a main pane is visible that displays views to perform various things (searching, editing, or whatever you app is supposed to do). In addition, the main pane displays a logout button. When the logout button is clicked the user returns to the login pane and must log back in.

Now that we have our log in and log out scenarios, let's start by putting our statechart together. The code is the following:

MyApp.statechart = Ki.Statechart.create({

  rootState: Ki.State.design({

    initialSubstate: "loggedOut",

    loggedOut: Ki.State.design({

      enterState: function() {
        // display the login pane
      },

      exitState: function() {
        // remove the login pane
      },

      logIn: function() {
        if (/* credentials are valid */) {
          this.gotoState('loggedIn');
        } else {
          // notify user that credentials are invalid
        }
      }

    }),

    loggedIn: Ki.State.design({

      enterState: function() {
        // display the main pane
      },

      exitState: function() {
        // remove the main pane
      },

      logOut: function() {
        this.gotoState('loggedOut');
      }

    })

  })

});

In the code above there are a few things going on. First, when the statechart is initialized the current state will be the loggedOut state. Next, you'll notice that both the loggedIn and loggedOut states have special enterState and exitState functions. You are not required to have them, but if you do they will be invoked during a state transition process whenever the gotoState method is invoked. Finally, the loggedIn and loggedOut states each have a function that represents an event they can react to: logOut and logIn, respectively. Neither state cares, nor should they care, how those events are sent to the statechart. Digging a bit further into the code, you'll notice that each state is responsible for a specific pane. The loggedOut state is responsible for the login pane and the loggedIn state is responsible for the main pane.

With our statechart built the next step is to implement our two panes. The code for the two panes, greatly simplified, is the following:

MyApp.loginPane = SC.MainPane.create({

  defaultResponder: 'MyApp.statechart',

  childViews: 'loginButton'.w(),

  loginButton: SC.ButtonView.design({
    action: 'logIn'
  })

});

MyApp.mainPane = SC.MainPane.create({

  defaultResponder: 'MyApp.statechart',

  childViews: 'logoutButton'.w(),

  logoutButton: SC.ButtonView.design({
    action: 'logOut'
  })

});

Both panes have their defaultResponder property set to our statechart. So for any action fired by a child view that does not have a target set, the statechart will then handle it. Note that the logoutButton and loginButton don't have any knowledge about what state within the statechart will react to the action being fired. This is what you want since it completely decouples the views from the states. Therefore if you make modifications to the statechart it does not affect the views.

When the user clicks the login button, the loggedOut state will react to the logIn event and make a transition to the loggedIn state, so long as the user credentials are valid. And when the user clicks the logout button, the loggedIn state will react to the logOut event and make a transition back to the loggedOut state.

Hopefully this starts to give you a better idea of how to apply Ki to your SproutCore application even though it's a pretty basic example. As well, just like the last A-B-C-D statechart example before, there is no use of concurrent states, advanced event handling, state plugin, state name spacing, and so on.

Clone this wiki locally