Marionette.Toolkit.StateMixin
StateMixin is a public mixin for App or any Marionette class definition that uses a Backbone.Model for keeping state.
Use a StateMixin if your object/view needs to maintain information that isn’t business data. This provides a consistent method for storing and getting state, along with triggering related events.
Documentation Index
- Using StateMixin
- Setting default state
- StateMixin’s
StateModel - StateMixin’s
stateEvents - StateMixin API
Using StateMixin
While StateMixin comes pre-mixined with Marionette.Toolkit.App and Marionette.Toolkit.Component, you can extend your own class with StateMixin by calling initState in your class’s initialize passing any desired options.
const MyStateModel = Backbone.Model.extend({});
const myClass = MnObject.extend({
StateModel: MyStateModel
initialize(options) {
this.initState(options);
}
});
_.extend(myClass.prototype, StateMixin)
You can also use Marionette.Toolkit.mixinState which is a utility to mixin the StateMixin into any Marionette.MnObjects or Marionette.Views. If there is no StateModel definition on your class then the StateModel will be defined as a vanilla Backbone.Model. However, if you have already defined StateModel on your class, your StateModel definition will not be overwritten.
const MyStateModel = Backbone.Model.extend({});
const myClass = MnObject.extend({
StateModel: MyStateModel
initialize(options) {
this.initState(options);
}
});
mixinState(MyClass);
Setting default state
Because the StateModel of the StateMixin has to be a Backbone.Model, it has access to model defaults. defaults should be defined on the StateModel definition.
const MyToolKitApp = App.extend({
StateModel: {
defaults: {
fooState: 'bar'
}
}
});
const myToolkitApp = new MyToolKitApp();
myToolkitApp.getState('fooState') === 'bar';
StateMixin’s StateModel
Define a StateModel on your class definition or pass as an option when calling initState(options). This must be
a Backbone.Model object definition, not an instance. If you do not
specify a StateModel, a vanilla Backbone.Model definition will be used.
Declared on Class
const MyStateModel = Backbone.Model.extend({});
const MyClass = MnObject.extend({
StateModel: MyStateModel
initialize(options) {
this.initState(options);
}
});
Passed as Option on Initialization
const MyStateModel = Backbone.Model.extend({});
const MyClass = MnObject.extend({
initialize(options) {
this.initState(options);
}
});
const myClass = new MyClass({
StateModel: MyStateModel
});
You can also define StateModel as a function. In this form, the value
returned by this method is the StateModel class that will be instantiated.
When defined as a function, it will receive the options passed to the constructor.
const MyStateModel = Backbone.Model.extend({});
App.extend({
StateModel(options){
if(options.foo){
return MyStateModel;
}
return Backbone.Model;
}
});
Passed as Option on Initialization
Alternatively, you can specify a StateModel in the options for
the constructor:
const MyToolKitApp = App.extend({...});
new MyToolKitApp({
StateModel: MyStateModel
});
StateMixin’s State
Optionally define a state attributes object on your class initialization or pass as an option when calling initState(options).
const MyStateModel = Backbone.Model.extend({});
const MyClass = MnObject.extend({
StateModel: MyStateModel
initialize(options) {
this.initState(options);
}
});
new MyClass({
state: {
foo: 'bar'
}
});
StateMixin’s stateEvents
StateMixin can bind directly to state events in a declarative manner:
const MyToolKitApp = App.extend({
stateEvents: {
'change': 'stateChanged'
},
stateChanged(model, options){
console.log('Changed!');
}
});
const myToolkitApp = new MyToolKitApp();
// will log "Changed!"
myToolkitApp.setState('foo', 'bar');
For more information on the various declarative options, see the
implementations of modelEvents and collectionEvents in the Marionette.View documentation.
StateMixin API
Setting State
StateMixin has a setState method that exposes the Backbone.Model.set
for the StateMixin’s attached StateModel. Implementation will match Backbone.Model.set documentation.
const myToolKitApp = new App({
stateEvents: {
'change:foo': 'alert'
},
alert(){
console.log('alert!');
}
});
// This will trigger the "change:foo" event and log "alert!" to the console.
myToolKitApp.setState('foo', 'bar');
Resetting State
StateMixin has a resetStateDefaults method that sets the StateModel instance attributes back to the defined defaults. Implementation will match Backbone.Model.defaults documentation.
const MyStateModel = Backbone.Model.extend({
defaults: {
foo: 'bar'
}
});
const myToolKitApp = new App({
StateModel: MyStateModel
});
// This will trigger the "change:foo" event and log "alert!" to the console.
myToolKitApp.setState('foo', 'hello');
console.log(this.getState('foo')); // hello
myToolKitApp.resetStateDefaults();
console.log(this.getState('foo')); // bar
Getting State
StateMixin has a getState method that exposes the Backbone.Model.get
for the Statemixin’s attached StateModel. Implementation will match Backbone.Model.get documentation with the
exception that not passing any attribute to “get” will return the state model
instance.
const MyStateModel = Backbone.Model.extend({
defaults: {
foo: 'bar'
}
});
const myToolKitApp = new App({
StateModel: MyStateModel
});
myToolKitApp.start()
// returns "bar"
myToolKitApp.getState('foo');
// returns myToolKitApp's MyStateModel instance.
myToolKitApp.getState();
Toggling State
StateMixin has a toggleState method that sets the StateModel instance attribute to a boolean value.
Attributes that do not exist on the state will be created.
Not passing in a value will toggle the attribute’s current value, while non-boolean values will be coerced to true or false.
const myToolKitApp = new App();
myToolKitApp.setState('foo', true);
// sets "foo" attribute to false
myToolKitApp.toggleState('foo');
// coerces "bar" string into boolean, setting "foo" attribute to true
myToolKitApp.toggleState('foo', 'bar');
// sets a "baz" attribute on the state with a true value
myToolKitApp.toggleState('baz');
Checking State
StateMixin has a hasState method that checks the StateModel instance for a specified attribute.
Passing an attribute that does not exist on the state will return false.
const myToolKitApp = new App();
// returns false
myToolKitApp.hasState('foo')
myToolKitApp.setState('foo', 'bar');
// returns true
myToolKitApp.hasState('foo');
// coerces "bar" string into boolean, setting "foo" attribute to true
myToolKitApp.setState('foo', false);
// Still returns true
myToolKitApp.hasState('foo');
Binding State Events
StateMixin has a delegateStateEvents that will bind all events specified
in the stateEvents option. Implementation matches Backbone.View.delegateEvents.
const myToolKitApp = new App({
stateEvents: {
'change:foo': 'alert'
},
alert(){
console.log('alert!');
}
});
// This will trigger the "change:foo" event and log "alert!" to the console.
myToolKitApp.setState('foo', 'bar');
myToolKitApp.undelegateStateEvents();
// This will still trigger the `change:foo` event, but will NOT log 'alert!'
// in the console
myToolKitApp.setState('foo', 'baz');
myToolKitApp.delegateStateEvents();
// This will trigger the "change:foo" event and log "alert!" to the console
// once again.
myToolKitApp.setState('foo', 'bar');
Unbinding State Events
StateMixin has a undelegateStateEvents that will unbind all event listeners
specified on the StateModel option. Implementation matches Backbone.View.undelegateEvents.
const myToolKitApp = new App({
stateEvents: {
'change:foo': 'alert'
},
alert(){
console.log('alert!');
}
});
// This will trigger the "change:foo" event and log "alert!" to the console.
myToolKitApp.setState('foo', 'bar');
myToolKitApp.undelegateStateEvents();
// This will still trigger the `change:foo` event, but will NOT log 'alert!' in the console
myToolKitApp.setState('foo', 'baz');