Events and Triggers
Communicating data within and between views is one of the major challenges for
any application framework. The method chosen by Backbone was to provide a
framework for triggering and handling events fired by objects. The modelEvents
object we've already seen is an example of this from Backbone. Marionette takes
this a step further by providing a more powerful trigger framework on top of
Backbone's event handlers. With triggers, we are able to decouple the firing of
an event from its handler. Many views fire triggers by default, leaving it to
the developer to choose how (or if) they handle them.
Using Events
Marionette provides an events
object that allows us to listen to activity in
our view's template and respond to it with a specific method. For example, we
can listen to data entry or a button press and determine the method to call. For
example:
var MyView = Marionette.LayoutView.extend({
template: require('./events.html'),
ui: {
title: '.title',
save: '.save'
},
events: {
'keyup @ui.title': 'setTitle',
'blur @ui.title': 'setTitle',
'click @ui.save': 'saveForm'
},
setTitle: function(domEvent) {
this.model.set({
title: this.ui.title.val()
});
},
saveForm: function(domEvent) {
this.model.save();
}
});
This is a simple example of setting a model field and saving the data to the
server. It should be clear what methods are being called when we perform actions
on the page. The events
object must be bound at initialization and will cause
an error if any of the referenced methods don't exist on the view.
Triggers
What happens if we want to provide more generic behavior? We could want to build a base view for our application that gets extended by other views, providing events that can be listened to when we want to extend the behavior of the view. Forcing us to define all the methods when creating the base class would be overkill, especially in a language like JavaScript that aims for a certain amount of brevity and clarity.
Luckily Marionette gives us the triggers
framework. To use a trigger, we
simply define the name of the trigger to be fired and either provide listeners
or, for simplicity, a special method name that will be called. We'll rewrite our
above example to demonstrate:
var MyView = Marionette.LayoutView.extend({
template: require('./events.html'),
ui: {
title: '.title',
save: '.save'
},
triggers: {
'keyup @ui.title': 'set:title',
'blur @ui.title': 'set:title',
'click @ui.save': 'save:form'
},
onSetTitle: function(domEvent) {
this.model.set({
title: this.ui.title.val()
});
},
onSaveForm: function(domEvent) {
this.model.save();
}
});
As you can see, the changes were minor - we changes the events
object to
triggers
and specified some trigger names. Triggers are typically named
using the :
as word separators. This special syntax is recognized by
Marionette to work out which methods to call. As you can see, set:title
will
call onSetTitle
when triggered and save:form
will call onSaveForm
when
triggered.
Manually Firing Triggers
We can also manually fire triggers. A common pattern is to use a before:
trigger to alert listeners that an action is about to be performed. Take our
save example:
var MyView = Marionette.LayoutView.extend({
template: require('./events.html'),
ui: {
title: '.title',
save: '.save'
},
triggers: {
'keyup @ui.title': 'set:title',
'blur @ui.title': 'set:title',
'click @ui.save': 'save:form'
},
onSetTitle: function(domEvent) {
this.model.set({
title: this.ui.title.val()
});
},
onSaveForm: function(domEvent) {
this.triggerMethod('before:model:save');
var view = this;
this.model.save({
success: function() {
view.triggerMethod('model:save');
}
});
}
});
This style is used throughout Marionette to allow developers to bind hooks
before and after common actions occur. Some of the most common examples are the
render and show hooks: before:render
, render
, before:show
, show
.
Built-in Triggers
We can listen to these standard triggers just like any other:
var MyView = Marionette.LayoutView.extend({
template: require('./events.html'),
onBeforeRender: function() {
alert('This view is about to be rendered');
},
onRender: function() {
alert('This view was rendered');
}
});
The next section will cover the built in triggers in more detail.