marionette.handlebars

Using Marionette with Handlebars and Mustache templates.


Keywords
backbone, marionette, handlebars, mustache, templates, views, models, collections
License
MIT
Install
npm install marionette.handlebars@2.0.0

Documentation

Marionette.Handlebars

Marionette.Handlebars does exactly what its name suggests: it adds support for Handlebars and Mustache templates to Marionette.

Marionette.Handlebars supports precompiled templates as well. It does its job entirely behind the scenes – load it, and you are all set.

There really isn't much in terms of an API – nothing, in fact, except for an extension point in case you want to lazy-load some templates.

If you are a happy user of this project already, you can support its development by donating to it. You absolutely don't have to, of course, but perhaps it is something you might actually want to do.

Dependencies and setup

Marionette.Handlebars, somewhat unsurprisingly, depends on the Marionette stack (Underscore, Backbone, Marionette) and Handlebars. Include marionette.handlebars.js after those are loaded.

When loaded as a module (e.g. AMD, Node), Marionette.Handlebars does not export a meaningful value. It solely lives in the Marionette namespace.

The stable version of Marionette.Handlebars is available in the dist directory (dev, prod). If you use Bower, fetch the files with bower install marionette.handlebars. With npm, it is npm install marionette.handlebars.

Precompiled templates

If you have precompiled your templates, Marionette.Handlebars retrieves them from the Handlebars cache. You just need to set the template property of a Marionette view to the ID of the compiled template (derived from the name of the template file).

Lazy loading of templates

If you want to lazy-load some of your templates, you must implement a loader which works for your requirements – such as your URL scheme, for instance. Marionette.Handlebars just provides an extension point for you.

A word of caution: Loading templates on demand is terrible for the performance of an application. The additional overhead of an HTTP request, particularly on mobile, is way beyond acceptable levels for UI elements which the user is already waiting for. Lazy template loading can make sense, though, for elements which are pre-rendered, but not yet exposed to the user. Use it judiciously.

How to implement a loader

You need to override Marionette.TemplateCache.prototype.lazyLoadTemplate. Out of the box, the method doesn't do anything. Replace it with your own implementation. Here is what you need to know.

  • Your loader is called with the template ID as the first argument.

    The template ID is the value of the template property, or template constructor option, in your Marionette views. The template ID would be a selector if your templates were read from the DOM. But because you provide your own loader, it can be any string you choose.

  • Your loader also receives an options argument which is passed around by Marionette.

    That argument can initially be provided to Marionette.TemplateCache.get(), but is usually undefined. Have a look at the few bits of information in the Marionette documentation to get started.

    Overall, though, the options argument is not very useful. Marionette views never pass it in when requesting a template. At a minimum, you'd have to override Marionette.Renderer.render() to make it work at all.

  • The loader must return the raw template HTML if successful, or undefined if it fails to fetch the template.

    If you want to fetch and return compiled templates, you need to enable it explicitly.

  • Your loader must not be async.

    Yes, synchronous loading is terribly inefficient. But asynchronous template loading is beyond what a generic Handlebars integration can provide to Marionette views.

    That said, you can certainly fill in the blanks. If the templates are lazy-loaded asynchronously, then the views must be rendered async as well. Also, there should probably be a mechanism to prevent multiple, simultaneous requests for the same template URL. (I have seen Traffic Cop being mentioned as a helpful tool, but it seems abandoned. More here.)

    A very basic implementation of async loading can be seen in the AMD demo of Marionette.Handlebars.

Please be aware that the lazy loader is only called as a last resort. The search for a matching template begins in the Handlebars cache of precompiled templates, then moves on to the DOM. Only if the template is not found in any of these places, the lazy loader gets its turn.

Allowing compiled templates to be lazy-loaded

For security reasons, you cannot lazy-load compiled templates, ie executable Javascript code. If you want to go down that route, you must explicitly enable it:

Marionette.TemplateCache.allowCompiledTemplatesOverHttp = true;

This is a global setting. The lazyLoadTemplate() method is allowed to return a function then, in addition to strings.

An example

A basic loader might look like this:

Marionette.TemplateCache.prototype.lazyLoadTemplate = function ( templateId, options ) {
    var templateHtml,
        templateUrl = "templates/" + templateId + ".hbs";

    Backbone.$.ajax( {
        url: templateUrl,
        success: function( data ) {
            templateHtml = data;
        },
        async: false
    } );

    return templateHtml;
};

The loader in the AMD demo might give you some inspiration, too.

Build process and tests

If you'd like to fix, customize or otherwise improve the project: here are your tools.

Setup

npm and Bower set up the environment for you.

  • The only thing you've got to have on your machine is Node.js. Download the installer here.
  • Open a command prompt in the project directory.
  • Run npm install. (Creates the environment.)
  • Run bower install. (Fetches the dependencies of the script.)

Your test and build environment is ready now. If you want to test against specific versions of Backbone, edit bower.json first.

Running tests, creating a new build

Considerations for testing

To run the tests on remote clients (e.g. mobile devices), start a web server with grunt interactive and visit http://[your-host-ip]:9400/web-mocha/ with the client browser. Running the tests in a browser like this is slow, so it might make sense to disable the power-save/sleep/auto-lock timeout on mobile devices. Use grunt test (see below) for faster local testing.

Tool chain and commands

The test tool chain: Grunt (task runner), Karma (test runner), Mocha (test framework), Chai (assertion library), Sinon (mocking framework). The good news: you don't need to worry about any of this.

A handful of commands manage everything for you:

  • Run the tests in a terminal with grunt test.
  • Run the tests in a browser interactively, live-reloading the page when the source or the tests change: grunt interactive.
  • If the live reload bothers you, you can also run the tests in a browser without it: grunt webtest.
  • Run the linter only with grunt lint or grunt hint. (The linter is part of grunt test as well.)
  • Build the dist files (also running tests and linter) with grunt build, or just grunt.
  • Build continuously on every save with grunt ci.
  • Change the version number throughout the project with grunt setver --to=1.2.3. Or just increment the revision with grunt setver --inc. (Remember to rebuild the project with grunt afterwards.)
  • grunt getver will quickly tell you which version you are at.

Finally, if need be, you can set up a quick demo page to play with the code. First, edit the files in the demo directory. Then display demo/index.html, live-reloading your changes to the code or the page, with grunt demo. Libraries needed for the demo/playground should go into the Bower dev dependencies, in the project-wide bower.json, or else be managed by the dedicated bower.json in the demo directory.

The grunt interactive and grunt demo commands spin up a web server, opening up the whole project to access via http. So please be aware of the security implications. You can restrict that access to localhost in Gruntfile.js if you just use browsers on your machine.

Changing the tool chain configuration

In case anything about the test and build process needs to be changed, have a look at the following config files:

  • karma.conf.js (changes to dependencies, additional test frameworks)
  • Gruntfile.js (changes to the whole process)
  • web-mocha/_index.html (changes to dependencies, additional test frameworks)

New test files in the spec directory are picked up automatically, no need to edit the configuration for that.

Facilitating development

To my own surprise, a kind soul wanted to donate to one of my projects, but there hadn't been a link. Now there is.

Please don't feel obliged in the slightest. The license here is MIT, and so it's free. That said, if you do want to support the maintenance and development of this component, or any of my other open-source projects, I am thankful for your contribution.

Naturally, these things don't pay for themselves – not even remotely. The components I write aim to be well tested, performant, and reliable. These qualities may not seem particularly fascinating, but I put a lot of emphasis on them because they make all the difference in production. They are also rather costly to maintain, time-wise.

That's why donations are welcome, and be it as nod of appreciation to keep spirits up. Thank you!

Donate with Paypal

Release Notes

v2.0.0

  • Removed the separate AMD/Node builds in dist/amd. Module systems and browser globals are now supported by the same file, dist/marionette.handlebars.js (or .min.js)
  • Updated Marionette dependency to include Marionette 3
  • Exposed version in Marionette.TemplateCache.MarionetteHandlebarsVersion

v1.0.2

  • Updated Backbone and Handlebars dependencies

v1.0.1

  • Updated Backbone dependency

v1.0.0

  • Added support for lazy-loading precompiled templates
  • Exposed getPrecompiledTemplate
  • Added lazy-loaded, precompiled templates to demo

v0.1.0 - 0.1.1

  • Initial development, tests, documentation

License

MIT.

Copyright (c) 2015-2016 Michael Heim.