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 following example should provide some clarity:
var _ = require('underscore');
var Mn = require('backbone.marionette');
var MyView = Mn.View.extend({
template: _.template('<h1>Hello, <%- contextKey() %></h1>'),
templateContext: function() {
return {
contextKeyVar: this.getOption('contextKey'), // OK - "this" refers to view
contextKeyFunction: function() {
return this.getOption('contextKey'); // ERROR - "this" refers to data
}
};
}
});
var myView = new MyView({contextKey: 'world'});
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.
© 2017 Muted Solutions, LLC
Licensed under the MIT License.
https://marionettejs.com/docs/v3.2.0/template.html