Is a small javascript library to add state machine capacities to any Backbone or non-Backbone object.
Backbone.StateMachine
. A module that can be mixed into any object, giving this object the super-powers of a state machine.Backbone.StatefulView
. A Backbone view, augmented with state machine capabilities.
Backbone.StateMachine
is used as a mixin, somewhat like Backbone.Events
.
Let's declare a very simple state machine representing an HTML element that can be either hidden or visible.
This machine will have 3 states, init, hidden and visible, and 3 transitions :
init --['initialized']--> visible
hidden --['show']--> visible
visible --['hide']--> hidden
Also, in order to actually show or hide the element, everytime the state machine enters in state visible a method doShow will be called, and everytime it leaves the state visible, a method doHide will be called.
var element = {el: $('#myElement')};
// !!! note that `StateMachine` requires the `Events` mixin
_.extend(element, Backbone.StateMachine, Backbone.Events, {
states: {
visible: {enter: ['doShow'], leave: ['doHide']}, // All options see: 'state options'
hidden: {} // Declaring this is optional
},
transitions: {
init: {
initialized: {enterState: 'visible'}
},
visible: {
hide: {enterState: 'hidden'} // All options see: 'transition options'
},
hidden: {
show: {enterState: 'visible'}
}
},
doShow: function() { this.el.show(); },
doHide: function() { this.el.hide(); }
});
state options
enter
- array containing names of methods to call when entering the state (Optional).leave
- array containing names of methods to call when leaving the state (Optional).
transition options
enterState
- arrival state of the transition.callbacks
- array containing names of methods to call when transition is crossed (Optional).triggers
- Backbone event to trigger when transition is crossed (Optional).
Note : This is not demonstrated here, but you can also define a transition from one state to itself.
The state machines are always created in init
state, even if it isn't declared. Therefore, you should always declare at least one transition from state init
to another state.
// !!! this method needs to be called before the state machine can be used
element.startStateMachine();
// First, let's get the machine out from 'init' state
element.trigger('initialized');
element.currentState; // 'visible'
element.trigger('hide'); // a transition is triggered, the element should disappear
element.currentState; // 'hidden'
element.trigger('hide'); // event 'hide' while in state 'hidden' -> no transition
element.trigger('show', 'quick'); // extra arguments will be passed to the callbacks
Every time a transition is crossed, the state machine triggers a bunch of Backbone events. This way, you can setup an efficient event-based communication between the different parts of your application. Cool thing with using this method is that those different parts don't need to know each other.
element.bind('transition', function(leaveState, enterState) {
alert('Transition from state "'+leaveState+'" to state "'+enterState+'"');
});
element.bind('leaveState:hidden', function() {
// synchronize other objects in your application
bla.trigger('activate');
aView.render();
});
element.bind('enterState:hidden', function() {
// synchronize other objects in your application
bla.trigger('deactivate');
});
Also, if your transition defines the triggers
option, for example {triggers: 'showItAll'}
, an extra event 'showItAll' will be triggered when that transition is crossed :
element.bind('showItAll', function() {
// do stuff
});
If you want to hush-up the state machine, and prevent any of those events to be triggered, just set silent
to true
:
element.silent = true; // next transitions will happen in silence
element.trigger('show');
element.trigger('hide');
element.silent = false; // next transitions will trigger events as described above
When declaring the transitions of your state machine, you can use the wildcard character '*'
to match any state.
var obj = {};
_.extend(obj, Backbone.StateMachine, Backbone.Events, {
transitions: {
visible: { hide: {enterState: 'hidden'} },
hidden: { show: {enterState: 'visible'} },
// No matter the machine's state, 'panic' will always trigger a transition :
'*': { panic: {enterState: 'panicking'} }
}
});
You can use the method toState
to force the machine to a particular state. In that case, no transition will occur, but the enter
callbacks will be executed.
element.currentState; // 'hidden'
element.toState('visible'); // the callback 'doShow' is executed.
element.currentState; // 'visible'
Backbone.StatefulView
is a backbone view that has state machine capabilities, plus a few goodies.
To each state of the machine corresponds a css class on the view's el
. By default the css class name is the state.
This makes styling very easy, for example :
#myStatefulView.visible {
display: block;
}
#myStatefulView.hidden {
display: none;
}
It is possible to declare transition events exactly the same way as in the Backbone.View.events
hash. For example :
var MyView = Backbone.StatefulView.extend({
transitions: {
'idle': {
'click .activate': {enterState: 'active'} // transition will occur when clicking on '.activate'
}
},
initialize: function(opts) {
this.toState('idle'); // you need to set manually the initial state
}
});
state options
className
- a css class added to view'sel
when the view is in this state.
backbone.statemachine
requires backbone
(and therefore all its dependencies).
It is tested against backbone 0.9.2.
Any suggestion, comment, question - are welcome - contact me directly or open a ticket.
Any bug report, feature request, ... open a ticket ! To run tests, either open tests/tests.html
in a browser, or run them from NodeJS (requires PhantomJS):
npm install -g grunt
grunt
http://en.wikipedia.org/wiki/Finite-state_machine
http://upload.wikimedia.org/wikipedia/commons/c/cf/Finite_state_machine_example_with_comments.svg
- 'enterCb' and 'leaveCb' renamed to 'enter' and 'leave'
- now machine always created in 'init' state.
- added the wilcard characted '*' to match any state.
- the states of the machine don't need to be declared anymore, the state machine can deduce it from the transitions.
Backbone.StateMachine
now fully uses backbone's events system. Events now triggered withtrigger
, instead ofreceive
previously.- in
Backbone.StatefulView
, transitions can now contain DOM events, using the same syntax asBackbone.View.events
.
- Initial release
Copyright (c) 2012 Sébastien Piquemal
Licensed under the MIT license.