< >

Marionette.View

A View is a view that represents an item to be displayed with a template. This is typically a Backbone.Model, Backbone.Collection, or nothing at all.

Views are also used to build up your application hierarchy - you can easily nest multiple views through the regions attribute.

Note: From Marionette v3.x, Marionette.View replaces Marionette.LayoutView and Marionette.ItemView.

Documentation Index

Rendering a Template

The Marionette View implements a powerful render method which, given a template, will build your HTML from that template, mixing in model information and any extra template context.

Overriding render If you want to add extra behavior to your view's render, you would be best off doing your logic in the onBeforeRender or onRender handlers.

To render a template, set the template attribute on your view:

var Mn = require('backbone.marionette');
var _ = require('underscore');

var MyView = Mn.View.extend({
  tagName: 'h1',
  template: _.template('Contents')
});

var myView = new MyView();
myView.render();

For more detail on how to render templates, see the Template documentation.

Managing an Existing Page

Marionette is able to manage pre-generated pages - either static or server-generated - and treat them as though they were generated from Marionette.

To use the existing page, set the template attribute to false:

var Mn = require('backbone.marionette');

var MyView = Mn.View({
  el: '#base-element',
  template: false
});

Marionette will then attach all the usual event and ui handlers to the view using the existing HTML. Though the View has no template, you can still listen to the before:render and render events that will fire as usual when render is called - or when you execute region.show(view).

Setting a falsy template value

When using an existing page, Marionette explicitly looks for false - any other falsy value will cause Marionette to raise an error when attempting to render the template.

Laying out Views - Regions

The Marionette.View class lets us manage a hierarchy of views using regions. Regions are a hook point that lets us show views inside views, manage the show/hide lifecycles, and act on events inside the children.

This Section only covers the basics. For more information on regions, see the Regions Documentation.

Regions are ideal for rendering application layouts by isolating concerns inside another view. This is especially useful for independently re-rendering chunks of your application without having to completely re-draw the entire screen every time some data is updated.

Regions can be added to a View at class definition, with regions, or at runtime using addRegion.

When you extend View, we use the regions attribute to point to the selector where the new view will be displayed:

var Mn = require('backbone.marionette');

var MyView = Mn.View.extend({
    template: '#tpl-view-with-regions',

    regions: {
        firstRegion: '#first-region',
        secondRegion: '#second-region'
    }
});

If we have the following template:

<script type="x-template/underscore" id="tpl-view-with-regions">
  <div id="first-region"></div>
  <div id="second-region"></div>
  <div id="third-region"></div>
</script>

When we show views in the region, the contents of #first-region and #second-region will be replaced with the contents of the view we show. The value in the regions hash is just a jQuery selector, and any valid jQuery syntax will suffice.

Managing Sub-views

View provides a simple interface for managing sub-views with showChildView and getChildView:

Showing a view

To show a view inside a region, simply call showChildView(region, view). This will handle rendering the view's HTML and attaching it to the DOM for you:

var Mn = require('backbone.marionette');
var SubView = require('./subview');

var MyView = Mn.View.extend({
  template: '#tpl-view-with-regions',

  regions: {
    firstRegion: '#first-region'
  },

  onRender: function() {
    this.showChildView('firstRegion', new SubView());
  }
});

Accessing a child view

To access the child view of a View - use the getChildView(region) method. This will return the view instance that is current being displayed at that region, or null:

var Mn = require('backbone.marionette');
var SubView = require('./subview');

var MyView = Mn.View.extend({
  template: '#tpl-view-with-regions',

  regions: {
    firstRegion: '#first-region'
  },

  onRender: function() {
    this.showChildView('firstRegion', new SubView());
  },

  onSomeEvent: function() {
    var first = this.getChildView('firstRegion');
    first.doSomething();
  }
});

If no view is available, getChildView returns null.

Region availability

Any defined regions within a View will be available to the View or any calling code immediately after instantiating the View. This allows a View to be attached to an existing DOM element in an HTML page, without the need to call a render method or anything else, to create the regions.

However, a region will only be able to populate itself if the View has access to the elements specified within the region definitions. That is, if your view has not yet rendered, your regions may not be able to find the element that you've specified for them to manage. In that scenario, using the region will result in no changes to the DOM.

Efficient nested view structures

When your views get some more regions, you may want to think of the most efficient way to render your views. Since manipulating the DOM is performance heavy, it's best practice to render most of your views at once.

Marionette provides a simple mechanism to infinitely nest views in a single paint: just render all of the children in the onRender callback.

var Mn = require('backbone.marionette');

var ParentView = Mn.View.extend({
  onRender: function() {
    this.showChildView('header', new HeaderView());
    this.showChildView('footer', new FooterView());
  }
});

myRegion.show(new ParentView(), options);

In this example, the doubly-nested view structure will be rendered in a single paint.

This system is recursive, so it works for any deeply nested structure. The child views you show can render their own child views within their onRender callbacks!

Listening to events on children

Using regions lets you listen to the events that fire on child views - views attached inside a region. This lets a parent view take action depending on what is happening in views it directly owns.

To see more information about events, see the events section

Defining childViewEvents

The childViewEvents hash defines the events to listen to on a view's children mapped to the method to call. The method will receive a child object referencing the view that triggered the event.

var Mn = require('backbone.marionette');

var MyView = Mn.View.extend({
  regions: {
    product: '.product-hook'
  },

  childViewEvents: {
    'add:item': 'updateBasketValue'
  },

  /** Assume item is the model belonging to the child */
  updateBasketValue: function(child, item) {
    var initialValue = this.model.get('value');
    this.model.set({
      value: item.get('value') * item.get('quantity')
    });
  }
});

You can also directly define a function to call in childViewEvents like so:

var Mn = require('backbone.marionette');

var MyView = Mn.View.extend({
  regions: {
    product: '.product-hook'
  },

  childViewEvents: {
    'add:item': function(child, item) {
      var initialValue = this.model.get('value');
      this.model.set({
        value: item.get('value') * item.get('quantity')
      });
    }
  }
});

Organizing your View

The View provides a mechanism to name parts of your template to be used throughout the view with the ui attribute. This provides a number of benefits:

  1. Provide a reference to commonly used UI elements
  2. Cache the jQuery selector
  3. Change the selector later in only one place in your view

Defining ui

To define your ui hash, just set an object of key to jQuery selectors to the ui attribute of your View:

var Mn = require('backbone.marionette');

var MyView = Mn.View.extend({
  template: '#my-template',
  ui: {
    save: '#save-button',
    close: '.close-button'
  }
});

Inside your view, the save and close references will point to the jQuery selectors #save-button and .close-button respectively.

Accessing UI elements

To get the handles to your UI elements, use the getUI(ui) method:

var Mn = require('backbone.marionette');

var MyView = Mn.View.extend({
  template: '#my-template',
  ui: {
    save: '#save-button',
    close: '.close-button'
  },

  onDoSomething: function() {
    var saveButton = this.getUI('save');
    saveButton.addClass('disabled');
    saveButton.attr('disabled', 'disabled');
  }
});

As saveButton here is a jQuery selector, you can call any jQuery methods on it, according to the jQuery documentation.

Referencing UI in events

The UI attribute is especially useful when setting handlers in the events and triggers objects - simply use the @ui. prefix:

var Mn = require('backbone.marionette');

var MyView = Mn.View.extend({
  template: '#my-template',
  ui: {
    save: '#save-button',
    close: '.close-button'
  },

  events: {
    'click @ui.save': 'handleSave'
  },

  triggers: {
    'click @ui.close': 'close:view'
  },

  handleSave: function() {
    this.model.save();
  }
});

In this example, when the user clicks on #save-button, handleSave will be called. If the user clicks on .close-button, then the event close:view will be fired on MyView.

By prefixing with @ui, we can change the underlying template without having to hunt through our view for every place where that selector is referenced - just update the ui object.

Events

Firing events on views allows you to communicate that something has happened on that view and allowing it to decide whether to act on it or not.

During the create/destroy lifecycle for a View, Marionette will call a number of events on the view being created and attached. You can listen to these events and act on them in two ways:

  1. The typical Backbone manner: view.on('render', function() {})
  2. Overriding the onEvent listener methods: onRender: function() {}

onEvent Listeners

Marionette creates onEvent listeners for all events fired using view.triggerMethod('event') - if there is an onEvent method, Marionette will call it for you. An example:

var Mn = require('backbone.marionette');

var MyView = Mn.View.extend({
  onRender: function() {
    console.log("Fired whenever view.triggerMethod('render') is called.");
  },

  onOtherEvent: function(argument) {
    console.log("Fired other:event with '" + argument + "' as an argument");
  }
});

var view = new MyView();

view.triggerMethod('other:event', 'test argument');

This will display in the console: Fired other:event with 'test argument' as an argument

To set up handlers for events, see the rules in the Documentation for Events.

Lifecycle Events

Marionette views defined a number of events during the creation and destruction lifecycle - when the view is displayed in and emptied from a region. In the documentation, we will reference the event name, though onEvent handling can be used.

View Creation Lifecycle

When a view is initialized and then displayed inside a region (using showChildView()) a set of events will be called in a specific order.

Order Event
1 before:render
2 render
3 before:attach
4 attach
5 dom:refresh

View Destruction Lifecycle

When region.empty() is called, the view will be destroyed, calling events as part of the destruction lifecycle.

Order Event
1 before:destroy
2 before:detach
3 detach
4 destroy

View Creation Events

These events are fired during the view's creation and rendering in a region.

View before:render

Triggered before a View is rendered.

var Mn = require('backbone.marionette');

Mn.View.extend({
  onBeforeRender: function() {
    // set up final bits just before rendering the view's `el`
  }
});
View render

Triggered after the view has been rendered. You can implement this in your view to provide custom code for dealing with the view's el after it has been rendered.

var Mn = require('backbone.marionette');

Mn.View.extend({
  onRender: function() {
    console.log('el exists but is not visible in the DOM');
  }
});
View before:attach

Triggered after the View has been rendered but just before it is first bound into the page DOM. This will only be triggered once per region.show() - if you want something that will be triggered every time the DOM changes, you may want render or before:render.

View attach

Triggered once the View has been bound into the DOM. This is only triggered once - the first time the View is attached to the DOM. If you want an event that triggers every time the DOM changes visibly, you may want dom:refresh

View dom:refresh

Triggered after the first attach event fires and every time the visible DOM changes.

Note for upgrading from Marionette 2 If you were using the show event - the dom:refresh event may be a better event than attach when you want to be sure something will run once your el has been attached to the DOM and updates.

View Destruction Events

These events are fired during the view's destruction and removal from a region.

View before:destroy

Triggered just prior to destroying the view, when the view's destroy() method has been called.

var Mn = require('backbone.marionette');

Mn.View.extend({
  onBeforeDestroy: function() {
    // manipulate the `el` here. it's already
    // been rendered, and is full of the view's
    // HTML, ready to go.
  }
});
View before:detach

The View will trigger the "before:detach" event when the view is rendered and is about to be removed from the DOM. If the view has not been rendered before, this event will not be fired.

View detach

The View will trigger the "detach" event when the view was rendered and has just been destroyed.

View destroy

Triggered just after the view has been destroyed. At this point, the view has been completely removed from the DOM.

var Mn = require('backbone.marionette');

Mn.View.extend({
  onDestroy: function() {
    // custom destroying and cleanup goes here
  }
});

Other View Events

These events are fired on specific actions performed on the view.

View before:add:region

When you add a region to a view - through addRegion() - the before:add:region event will fire just before the region is actually added.

View add:region

When a region is added to a view - through addRegion() - the add:region event will be fired. This happens just after the region is added and is available to use on the view.

var Mn = require('backbone.marionette');

var MyView = Mn.View.extend({
  onAddRegion: function(name, region) {
    console.log('Region called ' + name + ' was added');
  }
});

var myView = new MyView();
myView.addRegion('regionName', '#selector');
View before:remove:region

The View will trigger a "before:remove:region" event before a region is removed from the view. This allows you to perform any cleanup operations before the region is removed.

var Mn = require('backbone.marionette');

var MyView = Mn.View.extend({
  onBeforeRemoveRegion: function(name) {
    console.log('Region called ' + name + ' is about to be removed');
  },

  regions: {
    regionName: '#selector'
  }
});

var myView = new MyView();
myView.removeRegion("foo");
View remove:region

The View will trigger a "remove:region" event when a region is removed from the view. This allows you to use the region instance one last time, or remove the region from an object that has a reference to it:

var Mn = require('backbone.marionette');

var view = new Mn.View();

view.on("remove:region", function(name, region) {
  // add the region instance to an object
  delete myObject[name];
});

view.addRegion("foo", "#bar");

view.removeRegion("foo");

Binding To User Input

Views can bind custom events whenever users perform some interaction with the DOM. Using the view events and triggers handlers lets us either bind user input directly to an action or fire a generic trigger that may or may not be handled.

View events

The view events attribute binds DOM events to functions or methods on the view. The simplest form is to reference a method on the view:

var Mn = require('backbone.marionette');

var MyView = Mn.View.extend({
  events: {
    'click a': 'showModal'
  },

  showModal: function(event) {
    console.log('Show the modal');
  }
});

The DOM event gets passed in as the first argument, allowing you to see any information passed as part of the event.

When passing a method reference, the method must exist on the View.

Defining Listeners

Listeners are defined as:

eventname jqueryselector

You can also pass just the eventname part causing the event handler to fire on the entire view. This is especially useful for buttons and click handlers:

var Mn = require('backbone.marionette');

var ButtonView = Mn.View.extend({
  tagName: 'button',

  events: {
    click: 'showAlert'
  },

  showAlert: function() {
    alert('Button was clicked');
  }
});
Passing a Function

The events attribute can also directly bind functions:

var Mn = require('backbone.marionette');

var MyView = Mn.View.extend({
  events: {
    'click a': function(event) {
      console.log('Show the modal');
    }
  }
});

As when passing a string reference to a view method, the events attribute passes in the event as the argument to the function called.

View triggers

The view triggers attribute binds DOM events to Marionette View events that can be responded to at the view or parent level. For more information on events, see the events documentation. This section will just cover how to bind these events to views.

var Mn = require('backbone.marionette');

var MyView = Mn.View.extend({
  triggers: {
    'click a': 'link:clicked'
  },

  onLinkClicked: function() {
    console.log('Show the modal');
  }
});

When the a tag is clicked here, the link:click event is fired. This event can be listened to using the Magic Method Binding technique discussed in the events documentation.

Defining Listeners

Listeners are defined as:

eventname jqueryselector

You can also pass just the eventname part causing the event handler to fire on the entire view. This is especially useful for buttons and click handlers:

var Mn = require('backbone.marionette');

var ButtonView = Mn.View.extend({
  tagName: 'button',

  events: {
    click: 'entire:view:clicked'
  },

  onEntireViewClicked: function() {
    alert('Button was clicked');
  }
});
Event Bubbling

The major benefit of the triggers attribute over events is that triggered events can bubble up to any parent views. For a full explanation of bubbling events and listening to child events, see the event bubbling documentation.

Model and Collection events

The Marionette View can bind to events that occur on attached models and collections - this includes both [standard][backbone-events] and custom events.

Model Events

For example, to listen to a model's events:

var Mn = require('backbone.marionette');

var MyView = Mn.View.extend({
  modelEvents: {
    'change:attribute': 'actOnChange'
  },

  actOnChange: function(value, model) {
    console.log('New value: ' + value);
  }
});

The modelEvents attribute passes through all the arguments that are passed to model.trigger('event', arguments).

The modelEvents attribute can also take a function returning an object.

Function callback

You can also bind a function callback directly in the modelEvents attribute:

var Mn = require('backbone.marionette');

var MyView = Mn.View.extend({
  modelEvents: {
    'change:attribute': function() {
      console.log('attribute was changed');
    }
  }
})

Collection Events

Collection events work exactly the same way as modelEvents with their own collectionEvents key:

var Mn = require('backbone.marionette');

var MyView = Mn.View.extend({
  collectionEvents: {
    sync: 'actOnSync'
  },

  actOnSync: function(collection) {
    console.log('Collection was synchronised with the server');
  }
});

The collectionEvents attribute can also take a function returning an object.

Just as in modelEvents, you can bind function callbacks directly inside the collectionEvents object:

var Mn = require('backbone.marionette');

var MyView = Mn.View.extend({
  collectionEvents: {
    'update': function() {
      console.log('the collection was updated');
    }
  }
})

Listening to both

If your view has a model and collection attached, it will listen for events on both:

var Mn = require('backbone.marionette');

var MyView = Mn.View.extend({
  modelEvents: {
    'change:someattribute': 'changeMyAttribute'
  },

  collectionEvents: {
    update: 'modelsChanged'
  },

  changeMyAttribute: function() {
    console.log('someattribute was changed');
  },

  modelsChanged: function() {
    console.log('models were added or removed in the collection');
  }
})

In this case, Marionette will bind event handlers to both.

Advanced View Topics

This method is used to convert a View's model or collection into a usable form for a template.

Item Views are called such because they process only a single item at a time. Consequently, only the model or the collection will be serialized. If both exist, only the model will be serialized.

By default, models are serialized by cloning the attributes of the model.

Collections are serialized into an object of this form:

{
  items: [modelOne, modelTwo]
}

where each model in the collection will have its attributes cloned.

The result of serializeData is included in the data passed to the view's template.

Let's take a look at some examples of how serializing data works.

var myModel = new MyModel({foo: "bar"});

new MyView({
  template: "#myItemTemplate",
  model: myModel
});

MyView.render();
<script id="myItemTemplate" type="template">
  Foo is: <%= foo %>
</script>

If the serialization is a collection, the results are passed in as an items array:

var myCollection = new MyCollection([{foo: "bar"}, {foo: "baz"}]);

new MyView({
  template: "#myCollectionTemplate",
  collection: myCollection
});

MyView.render();
<script id="myCollectionTemplate" type="template">
  <% _.each(items, function(item){ %>
    Foo is: <%= foo %>
  <% }); %>
</script>

If you need to serialize the View's model or collection in a custom way, then you should override either serializeModel or serializeCollection.

On the other hand, you should not use this method to add arbitrary extra data to your template. Instead, use View.templateContext.

Improve this page