So far, our users#show
action pulls down both a User
model and a Repos
collection for that model. If we were to navigate from users#index
to users#show
, we already have that user model cached in memory (because we fetched it in order to render the list), but we have to make a roundtrip to the server to fetch the Repos
, which aren't part of the User
attributes. This means that instead of immediately rendering the users/show
view, we wait for the Repos
API call to finish. But what if instead we want to lazy-load the Repos
so we can render that view immediately for a better user experience?
We can achieve this by lazy-loading models or collections in our subviews. Check out the users#show_lazy
action, which demonstrates this approach:
// app/controllers/users_controller.js
module.exports = {
// ...
show_lazy: function(params, callback) {
var spec = {
model: {model: 'User', params: params}
};
this.app.fetch(spec, function(err, result) {
if (err) return callback(err);
// Extend the hash of options we pass to the view's constructor
// to include the `template_name` option, which will be used
// to look up the template file. This is a convenience so we
// don't have to create a separate view class.
_.extend(result, {
template_name: 'users/show_lazy'
});
callback(err, 'users/show', result);
});
}
}
The first thing to notice is that in our fetch spec
, we only specify the User
model, leaving out the Repos
collection. Then, we tell the view to use a different template than the default. We do this by passing in a template_name
property to the view's options, which is passed to its constructor. We extend the result
object to have this; the third argument to our callback
is an object that's passed to the view's constructor. We could have also created a separate view class in JavaScript for this, to match our new template.
Here's the users/show_lazy
template, abbreviated:
<!-- app/templates/users/show_lazy.hbs -->
...
<div class="span6">
{{view "user_repos_view" collection_name="Repos" param_name="login" param_value=login lazy="true"}}
</div>
<div class="span6">
...
</div>
So, the only difference to our original users/show
template is that instead of passing collection=repos
to our user_repos_view
subview, we pass collection_name="Repos" param_name="login" param_value=login lazy="true"
. When fetching collections, we specify params, which are used to fetch and cache the models for that collection. We quote all of these arguments except for param_value=login
; quoted arguments are passed in as string literals, and unquoted arguments are references to variables that are available in the current Handlebars scope. login
is one of the attributes of a User
model, which gets passed into the template. The lazy="true"
tells the view that it needs to fetch (or find a cached version of) the specified model or collection.
We can see this at play in our app if we add a route in our app/routes.js
file that routes users_lazy/:login
to users#show_lazy
, and change our app/templates/users_index_view.hbs
to link to /users_lazy/{{login}}
.
Now, if we click from the list of users on users#index
, you'll see the page gets rendered immediately, and the repos are rendered once the API call finishes. If you click back and forward in your browser, you see it's cached.