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:
onBeforeTransition
onBefore<TRANSITION>
onLeaveState
onLeave<STATE>
onTransition
All subsequent lifecycle events will be cancelled and the state will remain unchanged.