I'm trying to wrap my head around the model
property or a route vs. the content
property of the controller. If you set the model
property in a route, does this automatically set it to the content
property in a generated controller.
Also, I think the content
property of the controller allows you to access the attributes of that object in the template, is that true?
I read the docs and still am having trouble digesting some of these conventions.
As answered here, Ember.Route
has a model
function which allows you to set an object or a collection of objects as the model of that route. Routes that deal with a single object should have a controller that extends Ember.ObjectController
while routes that deal with a collection of objects should have a controller that extends Ember.ArrayController
.
Subsequently, in the Route
workflow, the data coming from the model
hook is set into the controller's content
property via setupController
hook.
The routes have their own workflow to setup their controllers, so by default this method will be called and populate the content with the model. Consider the following:
App.Email = DS.Model.extend({
address: DS.attr('string'),
isActive: DS.attr('boolean')
});
App.Router.map(function() {
this.resource('emails', function() {
this.route('email', {path: ':email_id'});
});
});
App.EmailsRoute = Ember.Route.extend({
model: function() {
return App.Email.find();
}
});
App.EmailRoute = Ember.Route.extend({
model: function(params) {
return App.Email.find(params.email_id);
}
});
App.EmailsController = Ember.ArrayController.extend();
App.EmailController = Ember.ObjectController.extend();
The framework should generate the default code for these routes in order to setup the controller, which would look like this (and you can override if you want):
App.EmailsRoute = Ember.Route.extend({
...
setupController: function(controller, model) {
controller.set('content', model);
}
...
});
There are cases (see question/answer linked above) in which you may need/want to override these methods to do something different than the default functionality, for example:
App.EmailsRoute = Ember.Route.extend({
model: function(params) {
return [{id: 1, address: 'other@email.com'}];
},
setupController: function(controller, model) {
// here, controller is whatever controller this route needs
// by conventions, it knows it should be EmailsController
// of the type ArrayController
// model is whatever was returned by the model function above
// the content is a "bag" which can be filled with a model or any
// other object you need. Just keep in mind your view layer will
// be referring to this object later on
controller.set('content', model);
// you can set other properties of the controller here too
controller.set('applyFilter', true);
}
});
Now the templates will be able to access the data in the controller. The example below iterates through a collection of objects (App.Email
) in the EmailsController
. Any public attribute in this collection or in its child objects are accessible here, one example is {{email.address}}
:
<script type="text/x-handlebars" data-template-name="emails">
<ul>
{{#each email in controller}}
<li>
{{#linkTo emails.email email}}
{{email.address}}
{{/linkTo}}
</li>
{{/each}}
</ul>
{{outlet}}
</script>
Note that the template is not talking directly to the model, but rather to the content
, which was assigned with data from the model. Like I said, you can stash any object in the content or model via routes, so you're not tied to use DS.Model
nor the architecture is strongly coupled.
If this model, instead of App.Email
type, had a different type with different attributes, it would also be accessible here, with limitations tho. If an attribute of the model is a collection, it cannot be accessed through index (e.g. {{email.messages[0].body}}
wouldn't work). The best course of action in this case, would be a computed property in the controller which would give you direct access to the first item of the messages collection of the email, if it had one.