The Marionette View's primary purpose is to render your model and collection
data into the template you assign it. The basic syntax for setting a template
is similar to the syntax for
Backbone.js View template
:
var Mn = require('backbone.marionette');
var _ = require('underscore');
var MyView = Mn.View.extend({
tagName: 'h1',
template: _.template('Contents')
});
var myView = new MyView();
myView.render();
This will cause the contents of the template
attribute to be rendered inside
a <h1>
tag.
If your index page contains a template element formatted for Underscore, you can
simply pass in the jQuery selector for it to template
and Marionette will look
it up:
var Mn = require('backbone.marionette');
export.MyView = Mn.View.extend({
template: '#template-layout'
});
<script id="template-layout" type="x-template/underscore"></script>
Marionette compiles the template above using _.template
and renders it for
you when MyView
gets rendered.
A more common way of setting a template is to assign a function to template
that renders its argument. This will commonly be the _.template
function:
var _ = require('underscore');
var Mn = require('backbone.marionette');
export.MyView = Mn.View.extend({
template: _.template('<h1>Hello, world</h1>')
});
This doesn't have to be an underscore template, you can pass your own rendering function:
var Mn = require('backbone.marionette');
var Handlebars = require('handlebars');
var MyView = Mn.View.extend({
template: function(data) {
return Handlebars.compile('<h1>Hello, {{ name }}')(data);
}
});
Using a custom function can give you a lot of control over the output of your view after its context is calculated. If this logic is common, you may be best overriding your renderer to change your default template renderer.
getTemplate
functionThe getTemplate
function is used to choose the template to render after the
view has been instantiated. You can use this to change the template based on
some simple logic such as the value of a specific attribute in the view's model.
The returned value can be either a jQuery selector or a compiled template
function that will be called with the view's data and context.
var Mn = require('backbone.marionette');
var MyView = Mn.View.extend({
getTemplate: function(){
if (this.model.get('is_active')){
return '#template-when-active';
} else {
return '#template-when-inactive';
}
}
});
This differs from setting template
as this method must be executed and
calculated when the view is rendered. If your template is always the same, use
the template
attribute directly.
Marionette will happily render a template without a model. This won't give us a particularly interesting result. As with Backbone, we can attach a model to our views and render the data they represent:
var Bb = require('backbone');
var Mn = require('backbone.marionette');
var MyModel = Bb.Model.extend({
defaults: {
name: 'world'
}
});
var MyView = Mn.View.extend({
template: _.template('<h1>Hello, <%- name %></h1>')
});
var myView = new MyView({model: new MyModel()});
Now our template has full access to the attributes on the model passed into the view.
The Marionette.View
also provides a simple tool for rendering collections into
a template. Simply pass in the collection as collection
and Marionette will
provide an items
attribute to render:
var Bb = require('backbone');
var Mn = require('backbone.marionette');
var MyCollection = Bb.Collection.extend({
});
var MyView = Mn.View.extend({
template: '#hello-template'
});
var collection = new MyCollection([
{name: 'Steve'}, {name: 'Helen'}
]);
var myView = new MyView({collection: collection});
For clarity, we've moved the template into this script tag:
<script id="hello-template" type="x-template/underscore">
<ul>
<% _.each(items, function(item) { %>
<li><%- item.name %></li>
<% }) %>
</ul>
</script>
As you can see, items
is provided to the template representing each record in
the collection.
While possible, reacting to user interaction with individual items in your
collection is tricky with just a View
. If you want to act on individual items,
it's recommended that you use CollectionView
and handle the behavior at the individual item level.
Marionette uses a simple method to determine whether to make a model or collection available to the template:
view.model
is set, the attributes from model
view.model
is not set, but view.collection
is, set items
to the
individual items in the collectionThe result of this is mixed into the
templateContext
object and made available to your
template. Using this means you can setup a wrapper View
that can act on
collectionEvents
but will render its model
attribute - if your model
has
an items
attribute then that will always be used. If your view needs to serialize
by different rules override serializeData()
.
The Marionette.View
provides a templateContext
attribute that is used to add
extra information to your templates. This can be either an object, or a function
returning an object. The keys on the returned object will be mixed into the
model or collection keys and made available to the template.
Using the context object, simply attach an object to templateContext
as so:
var _ = require('underscore');
var Mn = require('backbone.marionette');
var MyView = Mn.View.extend({
template: _.template('<h1>Hello, <%- contextKey %></h1>'),
templateContext: {
contextKey: 'world'
}
});
var myView = new MyView();
The myView
instance will be rendered without errors even though we have no
model or collection - contextKey
is provided by templateContext
.
The templateContext
attribute can also
take a function.
The templateContext
object can also be a function returning an object. This is
useful when you want to access
information from the surrounding view (e.g. model methods).
To use a templateContext
, simply assign a function:
var _ = require('underscore');
var Mn = require('backbone.marionette');
var MyView = Mn.View.extend({
template: _.template('<h1>Hello, <%- contextKey %></h1>'),
templateContext: function() {
return {
contextKey: this.getOption('contextKey')
}
}
});
var myView = new MyView({contextKey: 'world'});
Here, we've passed an option that can be accessed from the templateContext
function using getOption()
. More information on getOption
can be found in
the documentation for Marionette.Object
.
this
When using functions in the templateContext
it's important to know that this
is bound to the result of serializeData()
and not the view. An
illustrative example:
var _ = require('underscore');
var Mn = require('backbone.marionette');
var MyView = Mn.View.extend({
template: _.template('<h1>Hello, <%- contextKey() %></h1>'),
templateContext: {
contextKey: function() {
return this.getOption('contextKey'); // ERROR
}
}
});
var myView = new MyView({contextKey: 'world'});
The above code will fail because the context object in the template
cannot see the view's getOption
. This would also apply to functions
returned by a templateContext
function, even though the function itself is
bound to the view context.
The serializeData
method is used to convert a View's model
or collection
into a usable form for a template. It follows the Model/Collection Rendering Rules
to determine how to serialize the data.
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.