Ractive.JS

Dynamic Templating with Components


The Problem

This has been an astronomically huge topic in the Ractive community. Okay maybe not that big but I needed it and also saw that someone else posted about it so I figurred I’d write up a solution. This solution is great because it works now and it also works with the way that dynamic templating will likely work in the future.

Dynamic templating(in the Ractive world) is being able to modify a piece or all of a template based upon data in your data set. Typically when you have an unknown and also want a half way decent architecture with readable templates. You could attach them all as partials, and do some complex expressions to output the correct template but this means you’re required to modify the template when things change. Example: A display grid with different types of data cells, and inline editing with their own complex templates.

The First Pass

We first solved this issue without the dynamic templating. The way the data was setup was having a group of columns that mapped to a particular key on the data row object. We looped over each column in JavaScript, determined what base template to use and with regular expressions we replaced HTML Comment tokens with both the column index and key for the data row. Our templates also contained the inline editing logic as well. Due to our data structure our templates ended up being four templates in one. Two sets for the display and two sets for the editing portion. Finally we mashed these all together in the correct order, slapped a row loop around the whole block and walked away. Suffice to say it was fast in some browsers, and slow in others but it worked. It worked are not the only words you want to use when developing a new architecture in a new library.

The Solution

We value performance so after getting it to work we turned towards doing it better. In future Ractive the partials will likely live on the data attribute. That way we can use the templating logic to point to the correct template, if the data changes to reference a new partial then we can teardown the old and replace it with the new. Ractive isn’t set up for DOM diffing yet but hopefully this in an applicable area to utilize it. After doing some analysis we found that templates took too long to parse, large lists caused two-way data binding to be slow, toggling the edit state to be slow, and rendering large amounts of rows took way too long.

We did a few things.

  1. Took each template and converted it into as functionally small as possible. Each template was broke into display pieces and edit pieces.
  2. Created a row editor rather than the grid templates also being in charge of online editing. Separation of concerns.
  3. Preparsed the templates with Require.JS optimizer

The Dynamic Template Component Code

The reason you actually read this article.

The Component

var DynamicTemplateComponent = Ractive.extend({
    template: '{{> dynamiccell}}',
    setTemplate: function(options) {
     options.partials.dynamiccell = options.data.template;
    },
    beforeInit: function(options) {
        this.setTemplate(options);
    }
});

Example Template

{{#items}}
    <formElement value=”{{value}}” template=”{{template}}”/>
{{/items}}

Example Data

var data = {
 items: [{
      template: ‘<h1>{{value}}</h1>’, 
      value: ‘This is a title’
}, {
      template: ‘<input type=”text” value=”{{value}}” style=”display:block; clear: both;” />’,
      value: ‘Input Value’
}, {
    template: ‘<textarea value=”{{value}}”></textarea>’,
    value: ‘Textarea Value’
 }]
}

Example Usage

var ractive = new Ractive({
 el: ‘body’,
 template: formTemplate,
 components: {
  formElement: DynamicTemplateComponent
 }
 data: data
});

The template partials are stored on the data object which sets you up for future changes in Ractive. You can setup preparsing of templates by taking the inline strings and turning them into files that are brought in with RequireJS text and the Ractive RV template preparsing optimizer. We still maintain 2 way data binding, events can still be emitted from the component.

The Conclusion

After implementing this method we saw immediate performance improvements across all browsers as well as cleaner templates, and appropriate separation of concerns. It’s a decent method but their are some drawbacks. If you have a significant amount of events they all need to be added to the component in an on-emitEvent=”parentBindEvent”. This is going to be handled in the future by adding an ‘on’ object that can be passed in the instantiation of Ractive. Components are new Ractive objects meaning a lot of them could potentially be instantiated. Memory likely won’t be an issue but it’s something to keep an eye on. Minor drawbacks that are being fixed hopefully in the near future.

Email me when Jason Brown publishes or recommends stories