In order to track or perform an action when a transition occurs, five general-purpose lifecycle events can be observed:
onBeforeTransition- fired before any transitiononLeaveState- fired when leaving any stateonTransition- fired during any transitiononEnterState- fired when entering any stateonAfterTransition- fired after any transition
In addition to the general-purpose events, transitions can be observed using your specific transition and state names:
onBefore<TRANSITION>- fired before a specific TRANSITION beginsonLeave<STATE>- fired when leaving a specific STATEonEnter<STATE>- fired when entering a specific STATEonAfter<TRANSITION>- fired after a specific TRANSITION completes
For convenience, the 2 most useful events can be shortened:
on<TRANSITION>- convenience shorthand foronAfter<TRANSITION>on<STATE>- convenience shorthand foronEnter<STATE>
Individual lifecycle events can be observed using an observer method:
fsm.observe('onStep', function() {
console.log('stepped');
});Multiple events can be observed using an observer object:
fsm.observe({
onStep: function() { console.log('stepped'); }
onA: function() { console.log('entered state A'); }
onB: function() { console.log('entered state B'); }
});A state machine always observes its own lifecycle events:
var fsm = new StateMachine({
init: 'A',
transitions: [
{ name: 'step', from: 'A', to: 'B' }
],
methods: {
onStep: function() { console.log('stepped'); }
onA: function() { console.log('entered state A'); }
onB: function() { console.log('entered state B'); }
}
});Observers will be passed a single argument containing a lifecycle object with the following attributes:
- transition - the transition name
- from - the previous state
- to - the next state
In addition to the lifecycle argument, the observer will receive any arbitrary arguments passed
into the transition method
var fsm = new StateMachine({
transitions: [
{ name: 'step', from: 'A', to: 'B' }
],
methods: {
onTransition: function(lifecycle, arg1, arg2) {
console.log(lifecycle.transition); // 'step'
console.log(lifecycle.from); // 'A'
console.log(lifecycle.to); // 'B'
console.log(arg1); // 42
console.log(arg2); // 'hello'
}
}
});
fsm.step(42, 'hello');Lifecycle event names always use standard javascipt camelCase, even if your transition and state names do not:
var fsm = new StateMachine({
transitions: [
{ name: 'do-with-dash', from: 'has-dash', to: 'has_underscore' },
{ name: 'do_with_underscore', from: 'has_underscore', to: 'alreadyCamelized' },
{ name: 'doAlreadyCamelized', from: 'alreadyCamelize', to: 'has-dash' }
],
methods: {
onBeforeDoWithDash: function() { /* ... */ },
onBeforeDoWithUnderscore: function() { /* ... */ },
onBeforeDoAlreadyCamelized: function() { /* ... */ },
onLeaveHasDash: function() { /* ... */ },
onLeaveHasUnderscore: function() { /* ... */ },
onLeaveAlreadyCamelized: function() { /* ... */ },
onEnterHasDash: function() { /* ... */ },
onEnterHasUnderscore: function() { /* ... */ },
onEnterAlreadyCamelized: function() { /* ... */ },
onAfterDoWithDash: function() { /* ... */ },
onAfterDoWithUnderscore: function() { /* ... */ },
onAfterDoAlreadyCamelized: function() { /* ... */ }
}
});To recap, the lifecycle of a transition occurs in the following order:
onBeforeTransition- fired before any transitiononBefore<TRANSITION>- fired before a specific TRANSITIONonLeaveState- fired when leaving any stateonLeave<STATE>- fired when leaving a specific STATEonTransition- fired during any transitiononEnterState- fired when entering any stateonEnter<STATE>- fired when entering a specific STATEon<STATE>- convenience shorthand foronEnter<STATE>onAfterTransition- fired after any transitiononAfter<TRANSITION>- fired after a specific TRANSITIONon<TRANSITION>- convenience shorthand foronAfter<TRANSITION>
Any observer can cancel a transition by explicitly returning false during any of the following
lifecycle events:
onBeforeTransitiononBefore<TRANSITION>onLeaveStateonLeave<STATE>onTransition
All subsequent lifecycle events will be cancelled and the state will remain unchanged.