The Marionette Event system provides a system for objects to communicate with each other in a uniform way. In Marionette, this involves one object triggering an event that another listens to. This is an extended from of the event handling system in Backbone, and is different than DOM related events. It is mixed in to every Marionette class.
The traditional event handling system in Backbone
is fully supported in Marionette. Marionette, however, provides an additional
event API using the triggerMethod method - the key difference between the two
is that triggerMethod automatically calls specially named event handlers.
triggerMethodJust like Backbone's trigger the
triggerMethod method fires the named event on the instance - any listeners will then
be triggered on the event. If there are no listeners, this call will still succeed.
All arguments after the first event name string will be passed to all event handlers.
import { View } from 'backbone.marionette';
const MyView = View.extend({
callMethod(myString) {
console.log(myString + ' was passed');
}
});
const myView = new MyView();
/* See Backbone.listenTo */
myView.on('something:happened', myView.callMethod);
/* Calls callMethod('foo'); */
myView.triggerMethod('something:happened', 'foo');
The triggerMethod method is available to all Marionette classes.
Marionette's event triggers work just like regular Backbone events - you can
use myView.on and myObject.listenTo to act on events:
import { View } from 'backbone.marionette';
const MyView = View.extend({
initialize() {
this.on('event:happened', this.logCall);
},
logCall(myVal) {
console.log(myVal);
}
});
You can also use listenTo as in Backbone:
import { View } from 'backbone.marionette';
const OtherView = View.extend({
initialize(someView) {
this.listenTo(someView, 'event:happened', this.logCall);
},
logCall(myVal) {
console.log(myVal);
}
});
const MyView = View.extend();
const myView = new MyView();
const otherView = new OtherView(myView);
myView.triggerMethod('event:happened', 'someValue'); // Logs 'someValue'
As in Backbone, listenTo will pass the object
it is called on in as the context variable. These behave exactly as in Backbone,
so using object.on will require you to unhook any event handlers yourself to
prevent memory leaks. Marionette, however, does provide extra helpers as part of
the view lifecycle that bind and unbind event handlers for you. this is the
core of onEvent Binding.
onEvent BindingThe major difference between Backbone.trigger and triggerMethod is
that triggerMethod can fire specially named events on the instance. For
example, a view that has been rendered will iternally fire view.triggerMethod('render')
and call onRender - providing a handy way to add behavior to your views.
Determining what method an event will call is easy, we will outline this with an
example using before:dom:refresh though this also works with any custom events
you want to fire:
: characters - so before, dom, refreshBefore, Dom, Refreshon - on, Before, Dom, RefreshonBeforeDomRefreshUsing this process, before:dom:refresh will call the onBeforeDomRefresh
method. Let's see it in action with a custom event:
import { View } from 'backbone.marionette';
const MyView = View.extend({
onMyEvent(myVal) {
console.log(myVal);
}
});
const myView = new MyView();
myView.triggerMethod('my:event', 'someValue'); // Logs 'someValue'
As before, all arguments passed into triggerMethod after the event name will make
their way into the event handler. Using this method ensures there will be no unexpected
memory leaks.
events and triggersViews can automatically bind DOM events to methods and View events with events
and triggers respectively:
import { View } from 'backbone.marionette';
const MyView = View.extend({
events: {
'click a': 'showModal'
},
triggers: {
'keyup input': 'data:entered'
},
showModal(event) {
console.log('Show the modal');
},
onDataEntered(view, event) {
console.log('Data was entered');
}
});
For more information, see the DOM interactions documentation.
Views can automatically bind to its model or collection with modelEvents
and collectionEvents respectively.
import { View } from 'backbone.marionette';
const MyView = View.extend({
modelEvents: {
'change:someattribute': 'onChangeSomeattribute'
},
collectionEvents: {
'update': 'onCollectionUpdate'
},
onChangeSomeattribute() {
console.log('someattribute was changed');
},
onCollectionUpdate() {
console.log('models were added or removed in the collection');
}
});
For more information, see the Entity events documentation.
The View and CollectionView
are able to monitor and act on events on any of their direct children. Any events fired
on a view are automatically propagated to their direct parents as well. Let's
see a quick example:
import { View, CollectionView } from 'backbone.marionette';
const Item = View.extend({
tagName: 'li',
triggers: {
'click a': 'select:item'
}
});
const Collection = CollectionView.extend({
tagName: 'ul',
childViewEvents: {
'select:item': 'itemSelected'
},
itemSelected(childView) {
console.log('item selected: ' + childView.model.id);
}
});
Events fired on a view bubble up to their direct parent views, calling any
event methods using the childview: prefix (more on that shortly) and any
methods bound to the childViewEvents attribute. This works for built-in
events, custom events fired with triggerMethod and bound events using
triggers.
NOTE Automatic event bubbling can be disabled by setting
childViewEventPrefix to false.
When using implicit listeners, the childview:* event prefix is used which
needs to be included as part of the handler:
import { View, } from 'backbone.marionette';
const MyView = View.extend({
triggers: {
click: 'click:view'
},
doSomething() {
this.triggerMethod('did:something', this);
}
});
const ParentView = View.extend({
regions: {
foo: '.foo-hook'
},
onRender() {
this.showChildView('foo', new MyView());
},
onChildviewClickView(childView) {
console.log('View clicked ' + childView);
},
onChildviewDidSomething(childView) {
console.log('Something was done to ' + childView);
}
})
NOTE triggers will automatically pass the child view as an argument to the parent view, however triggerMethod will not, and so notice that in the above example, the triggerMethod explicitly passes the child view.
CollectionViewThis works exactly the same way for the CollectionView and its childView:
import { View, CollectionView } from 'backbone.marionette';
const MyChild = View.extend({
triggers: {
click: 'click:child'
}
});
const MyList = CollectionView.extend({
onChildviewClickChild(childView) {
console.log('Childview ' + childView + ' was clicked');
}
});
You can customize the event prefix for events that are forwarded
through the view. To do this, set the childViewEventPrefix
on the view or collectionview. For more information on the childViewEventPrefix see
Event bubbling.
The default value for childViewEventPrefix is childview. Setting this property to
false will disable automatic event bubbling.
import Backbone from 'backbone';
import { CollectionView } from 'backbone.marionette';
import MyChildView from './my-child-view';
const myCollection = new Backbone.Collection([{}]);
const CollectionView = CollectionView.extend({
childViewEventPrefix: 'some:prefix',
childView: MyChildView
});
const collectionView = new CollectionView({
collection: myCollection
});
collectionView.on('some:prefix:render', function(){
// child view was rendered
});
collectionView.render();
The childViewEventPrefix can be provided in the view definition or
in the constructor function call, to get a view instance.
To call specific functions on event triggers, use the childViewEvents
attribute to map child events to methods on the parent view. This takes events
fired on child views - without the childview: prefix - and calls the
method referenced or attached function.
import { View } from 'backbone.marionette';
const MyView = View.extend({
triggers: {
click: 'view:clicked'
}
});
const ParentView = View.extend({
regions: {
foo: '.foo-hook'
},
childViewEvents: {
'view:clicked': 'displayMessage'
},
onRender() {
this.showChildView('foo', new MyView());
},
displayMessage(childView) {
console.log('Displaying message for ' + childView);
}
});
The childViewEvents attribute can also attach functions directly to be event
handlers:
import { View } from 'backbone.marionette';
const MyView = View.extend({
triggers: {
click: 'view:clicked'
}
});
const ParentView = View.extend({
regions: {
foo: '.foo-hook'
},
childViewEvents: {
'view:clicked'(childView) {
console.log('Function called for ' + childView);
}
},
onRender() {
this.showChildView('foo', new MyView());
}
});
CollectionView's childViewEventsimport { CollectionView } from 'backbone.marionette';
// childViewEvents can be specified as a hash...
const MyCollectionView = CollectionView.extend({
childViewEvents: {
// This callback will be called whenever a child is rendered or emits a `render` event
render() {
console.log('A child view has been rendered.');
}
}
});
A childViewTriggers hash or method permits proxying of child view events without manually
setting bindings. The values of the hash should be a string of the event to trigger on the parent.
childViewTriggers is sugar on top of childViewEvents much
in the same way that view triggers are sugar for view events.
import { View, CollectionView } from 'backbone.marionette';
// The child view fires a custom event, `show:message`
const ChildView = View.extend({
// Events hash defines local event handlers that in turn may call `triggerMethod`.
events: {
'click .button': 'onClickButton'
},
triggers: {
'submit form': 'submit:form'
},
onClickButton () {
// Both `trigger` and `triggerMethod` events will be caught by parent.
this.trigger('show:message', 'foo');
this.triggerMethod('show:message', 'bar');
}
});
// The parent uses childViewEvents to catch the child view's custom event
const ParentView = CollectionView.extend({
childView: ChildView,
childViewTriggers: {
'show:message': 'child:show:message',
'submit:form': 'child:submit:form'
},
onChildShowMessage (message) {
console.log('A child view fired show:message with ' + message);
},
onChildSubmitForm (childView) {
console.log('A child view fired submit:form');
}
});
const GrandParentView = View.extend({
regions: {
list: '.list'
},
onRender() {
this.showChildView('list', new ParentView({
collection: this.collection
}));
},
childViewEvents: {
'child:show:message': 'showMessage'
},
showMessage(childView) {
console.log('A child (' + childView + ') fired an event');
}
});
CollectionView's childViewTriggersimport { View, CollectionView } from 'backbone.marionette';
// The child view fires a custom event, `show:message`
const ChildView = View.extend({
// Events hash defines local event handlers that in turn may call `triggerMethod`.
events: {
'click .button': 'onClickButton'
},
// Triggers hash converts DOM events directly to view events catchable on the parent.
// Note that `triggers` automatically pass the first argument as the child view.
triggers: {
'submit form': 'submit:form'
},
onClickButton () {
// Both `trigger` and `triggerMethod` events will be caught by parent.
this.trigger('show:message', 'foo');
this.triggerMethod('show:message', 'bar');
}
});
// The parent uses childViewEvents to catch the child view's custom event
const ParentView = CollectionView.extend({
childView: ChildView,
childViewTriggers: {
'show:message': 'child:show:message',
'submit:form': 'child:submit:form'
},
onChildShowMessage (message) {
console.log('A child view fired show:message with ' + message);
},
onChildSubmitForm (childView) {
console.log('A child view fired submit:form');
}
});
Marionette Views fire events during their creation and destruction lifecycle.
For more information see the documentation covering the
View Lifecycle.