Best practice to cache rendered templates


#1

I am working on an Ember app. The page contains a infinite scroll list of files and a details pane. When a file is selected it is supposed to show a form to modify file details in the details pane.

// router.js
this.resource('files', function () {
  this.route('file', { path: '/:file_id' });
});

// templates/files.hbs
{{ #my-list-component params.. }}
  list-item-content
{{ /my-list-component }}
{{outlet}} // this is for the file details

There is no template for the file though. The requirement is that form template for a file is autogenerated on the server on request.

// routes/files/file
export default Ember.Route.extend({
  model: function(params) {
    return this.store.find('file', params.file_id);
  },
  afterModel: function(file, transition) {
    // make ajax call to the web service 
    // to get form template for the current file
    return Ember.$.ajax(config.APP.API_HOST + formPath).then(function(data) {
      ...
      Ember.TEMPLATES['files/file'] = Ember.Handlebars.compile(data);
      ...
    }
  }
});

There are only few different types of forms depending on the file type, but many files. Forms are quite big with many Ember components on them. It takes time to render them(there are also some calculations on render). Is there a nice way to cache already rendered forms? So that when a file is selected, it checks if this type of form was already rendered, gets it from cache and shows as is(binding will update data from current model)?


#2

I haven’t tried to do this before, but I’ve been thinking about how one might implement this in Ember as well because our current app is going to soon include “data views” that receive pretty arbitrary stuff from the server.

I decided it was probably best to wait until HTMLBars/Fastboot work lands in stable because both of those projects should include portions that make this easier to accomplish well.

If this is a long living Route (sounds as though it might be) it is possible to make details a CollectionView where only one item is visible at a time and items utilize itemController. The trouble there is that in a long list you end up with a lot of generated HTML hanging around, which can itself hurt you.

Alternatively, it might be valuable to create a cacheable-view ember-addon that takes an arbitrary view and controller, saves it’s state, disconnects the bindings, moves it’s view contents into a document fragment, and provides a method for restoring it later.


#3

You can probably use a simple object and promises to cache the compiled forms. Here’s some example code to show what I mean.

// routes/files/file

// will cache the compiled forms based on their formPath
var compiledFormPromises = {};

export default Ember.Route.extend({
  model: function(params) {
    return this.store.find('file', params.file_id);
  },
  afterModel: function(file, transition) {
    // make ajax call to the web service 
    // to get form template for the current file
    return this.getCompiledForm(formPath).then(function(compiledForm) {
      ...
      Ember.TEMPLATES['files/file'] = compiledForm;
      ...
    };
  },
  getCompiledForm: function(formPath) {
    // Only call the server if we haven't already retrieved the form
    if (compiledFormPromises[formPath] === undefined) {
       compiledFormPromises[formPath] = Ember.$.ajax(config.APP.API_HOST + formPath).then(function(data) {
         return Ember.Handlebars.compile(data);
       });
    }
    return compiledFormPromises[formPath];     
  }
});

#4

Thanks a lot for the good ideas! I am going to play around with CollectionView, I do realize there is a danger of ending up with lots of generated HTML and component objects, but my hope is that there are not too many different form types in my case so it should be fine.

I couldn’t find cacheable-view addon. Do you have a link?

Also could you elaborate a bit more about how HTMLBars can help to solve it. HTMLBars is almost there with the 1.10 release, so it would be great to reuse new functionality.

Thanks!


#5

Thanks for the answer. This is not exactly what I meant, in your example you are caching complied templates, but I actually wanted to cache already rendered ones.


#6

I was actually meaning you’d have to create an addon called cacheable-view that does what I was describing :wink:

If HTMLBars doesn’t improve performance enough, I might be building something similar soon myself to explore dealing with a situation where a particularly complex route is entered and left often.