A Behavior
provides a clean separation of concerns to your view logic,
allowing you to share common user-facing operations between your views.
Behaviors are particularly good at factoring out the common user, model and collection interactions to be utilized across your application.
The easiest way to see how to use the Behavior
class is to take an example
view and factor out common behavior to be shared across other views.
var Mn = require('backbone.marionette');
var MyView = Mn.View.extend({
ui: {
destroy: '.destroy-btn'
},
events: {
'click @ui.destroy': 'warnBeforeDestroy'
},
warnBeforeDestroy: function() {
alert('You are about to destroy all your data!');
this.destroy();
},
onRender: function() {
this.ui.destroy.tooltip({
text: 'What a nice mouse you have.'
});
}
});
Interaction points, such as tooltips and warning messages, are generic concepts.
There is no need to recode them within your Views so they are prime candidates
to be extracted into Behavior
classes.
var Mn = require('backbone.marionette');
var DestroyWarn = Mn.Behavior.extend({
// You can set default options
// just like you can in your Backbone Models.
// They will be overridden if you pass in an option with the same key.
defaults: {
message: 'You are destroying!'
},
ui: {
destroy: '.destroy-btn'
},
// Behaviors have events that are bound to the views DOM.
events: {
'click @ui.destroy': 'warnBeforeDestroy'
},
warnBeforeDestroy: function() {
var message = this.getOption('message');
window.alert(message);
// Every Behavior has a hook into the
// view that it is attached to.
this.view.destroy();
}
});
var ToolTip = Mn.Behavior.extend({
defaults: {
text: ''
},
ui: {
tooltip: '.tooltip'
},
onRender: function() {
this.ui.tooltip.tooltip({
text: this.getOption('text')
});
}
});
We've passed in a defaults
attribute that sets default options.
This will be covered in default soon. With the warning and tooltip
behaviors extracted, we just need to attach them to our view:
var Mn = require('backbone.marionette');
var MyView = Mn.View.extend({
behaviors: [DestroyWarn, ToolTip]
});
Each behavior will now be able to respond to user interactions as though the event handlers were attached to the view directly. In addition to using array notation, Behaviors can be attached using an object:
var Mn = require('backbone.marionette');
var MyView = Mn.View.extend({
behaviors: {
destroy: DestroyWarn,
tooltip: ToolTip
}
});
When we attach behaviors to views, we can also pass in options to add to the
behavior. This tends to be static information relating to what the behavior
should do. In our above example, we want to override the message to our
DestroyWarn
and Tooltip
behaviors to match the original message on the View:
var Mn = require('backbone.marionette');
var MyView = Mn.View.extend({
behaviors: [
{
behaviorClass: DestroyWarn,
message: 'You are about to destroy all your data!'
},
{
behaviorClass: ToolTip,
text: 'What a nice mouse you have.'
}
]
});
Using an object, we must define the behaviorClass
attribute to refer to our
behaviors and then add any extra options with keys matching the option we want
to override. Any passed options will override the defaults
passed.
Here is the syntax for declaring which behaviors get used within a View.
Behavior
are also passed through to the Behavior
during initialization.Behavior
under options
.var Mn = require('backbone.marionette');
var MyView = Mn.View.extend({
ui: {
destroy: '.destroy-btn'
},
behaviors: {
DestroyWarn: {
message: 'you are destroying all your data is now gone!'
},
ToolTip: {
text: 'what a nice mouse you have'
}
}
});
Now let's create the DestroyWarn
Behavior
.
var Mn = require('backbone.marionette');
var DestroyWarn = Mn.Behavior.extend({
// You can set default options
// just like you can in your Backbone Models.
// They will be overridden if you pass in an option with the same key.
defaults: {
message: 'You are destroying!'
},
// Behaviors have events that are bound to the views DOM.
events: {
'click @ui.destroy': 'warnBeforeDestroy'
},
warnBeforeDestroy: function() {
alert(this.options.message);
// Every Behavior has a hook into the
// view that it is attached to.
this.view.destroy();
}
});
And onto the Tooltip
behavior.
var Mn = require('backbone.marionette');
var ToolTip = Mn.Behavior.extend({
ui: {
tooltip: '.tooltip'
},
onRender: function() {
this.ui.tooltip.tooltip({
text: this.options.text
});
}
});
defaults
can be a hash
or function
to define the default options for your Behavior
. The default options will be overridden depending on what you set as the options per Behavior
. (This works just like a Backbone.Model
.)
Marionette.Behavior.extend({
defaults: function() {
return {
'deepSpace': 9
}
}
});
Marionette.Behavior.extend({
defaults: {
'dominion': 'invasion',
'doge': 'amaze'
}
});
The view
is a reference to the View
instance that the Behavior
is attached to.
Marionette.Behavior.extend({
handleDestroyClick: function() {
this.view.destroy();
}
});
In addition to extending a View
with Behavior
, a Behavior
can itself use
other Behaviors. The syntax is identical to that used for a View
:
var Mn = require('backbone.marionette');
var Modal = Mn.Behavior.extend({
behaviors: {
DestroyWarn: {
message: 'Whoa! You sure about this?'
}
}
});
Nested Behaviors act as if they were direct Behaviors of the parent Behavior
's
view instance.
The Behavior
class provides proxies for a selection of View
functionality.
This includes listening to events on the view, being able to handle events on
models and collections, and being able to directly interact with the attached
template.
Behaviors are powered by an event proxy. This means that any events that are
triggered on a View
are passed to all attached behaviors
. This includes:
triggerMethod
triggers
childViewTriggers
childView
These handlers work exactly as they do on View
-
see the View
documentation
Behaviors provide proxies to a number of the view event handling attributes including:
ui
As in views, events
and triggers
can use the ui
references in their
listeners. These can be defined on either the Behavior or the View:
var Mn = require('backbone.marionette');
var MyBehavior = Mn.Behavior.extend({
ui: {
saveForm: '.btn-save'
},
events: {
'click @ui.saveForm': 'saveForm'
},
modelEvents: {
invalid: 'showError'
},
saveForm: function() {
this.view.model.save();
},
showError: function() {
alert('You have errors');
}
});
The Behavior
has a number of proxies attributes that directly refer to the
related attribute on a view:
$
el
$el
In addition, each behavior is able to reference the view they are attached to
through the view
attribute:
var Mn = require('backbone.marionette');
var ViewBehavior = Mn.Behavior.extend({
onRender: function() {
if (this.view.model.get('selected')) {
this.$el.addClass('highlight');
}
else {
this.$el.removeClass('highlight');
}
}
});
Behaviors, like views, have a ui
attribute that can reference and cache DOM
elements, just as in the View
. For more detail, see the
ui
documentation for views.
If your ui
keys clash with keys on the attached view, references within the
behavior will always use the definition on the behavior itself. As views are
only peripherally aware of their behaviors, their ui
keys will not be changed
when accessed within the View
. For example:
var Mn = require('backbone.marionette');
var MyBehavior = Mn.Behavior.extend({
ui: {
saveForm: '.btn-save'
},
events: {
'click @ui.saveForm': 'saveForm' // .btn-save
},
saveForm: function() {
this.view.model.save();
}
});
var FirstView = Mn.View.extend({
behaviors: [MyBehavior],
ui: {
saveForm: '.btn-primary'
},
events: {
'click @ui.saveForm': 'checkForm' // .btn-primary
},
checkForm: function() {
// ...
}
});