Skip to content

Instantly share code, notes, and snippets.

@mattpardee
Created June 13, 2014 18:23
Show Gist options
  • Save mattpardee/6a7e66f53fc84bd21804 to your computer and use it in GitHub Desktop.
Save mattpardee/6a7e66f53fc84bd21804 to your computer and use it in GitHub Desktop.
Stuff

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment