Using precompiled handlebars templates with Marionette
Asked Answered
L

1

3

I'm using Marionette with requirejs and I would also like to use precompiled handlebars templates. How does this work?

Here my current setup:

require_main.js:

requirejs.config({
    baseUrl: "/",
    paths: {
        'text': 'vendor/javascripts/text',
        'backbone': "vendor/javascripts/backbone",
        'backbone.wreqr': "vendor/javascripts/backbone.wreqr",
        'backbone.babysitter': "vendor/javascripts/backbone.babysitter",
        'jquery': "vendor/javascripts/jquery",
        'jquery-ui': 'vendor/javascripts/jquery-ui',
        'json2': "vendor/javascripts/json2",
        'marionette': "vendor/javascripts/backbone.marionette",
        'underscore': "vendor/javascripts/underscore",
        'handlebars': "vendor/javascripts/handlebars"
    },

    shim: {
        'underscore': {
            exports: "_"
        },
        'backbone': {
            deps: ["jquery", "underscore", "json2"],
            exports: "Backbone"
        },
        'marionette': {
            deps: ["backbone"],
            exports: "Marionette"
        },
        'jquery-ui': ['jquery'],

    'handlebars': {
      exports: 'Handlebars'
    }
    }
});
require(["app"], function(MyApp){
    MyApp.start();
});

app.js:

define(['marionette', 'handlebars', 'text!compiled.handlebars'], function(Marionette, Handlebars, Template_one) {

    var MyApp = new Marionette.Application();

    MyApp.addRegions({
        mainRegion: "#main-region"
    });

    MyApp.StaticView = Marionette.ItemView.extend({
        template: Template_one(context)
    });

    MyApp.on("initialize:after", function(){
        var staticView = new MyApp.StaticView();
        MyApp.mainRegion.show(staticView);  
    });

});

in my app.js I can get evth. work just fine with non compiled templates, like this:

...
var template = Handlebars.compile(Template_one)
var html = template(context)
template: html
...

but how to do it right with compiled templates?

Legal answered 8/11, 2013 at 15:56 Comment(0)
T
16

Update: With Handlebars precompiler only

The reason I mentioned Grunt earlier is because it comes very handy for a LOT of things. So, in my opinion, it's very important to know/learn about it.

But you can achieve the exact same thing with the Handlebars precompiler alone:

$ handlebars templates/* -f js/precompiled.handlebars.js

You still have to integrate precompiled.handlebars.js in your RequireJS config, see at the end of the answer.

Original: With Grunt

You'll need the Grunt Task Runner for that, it makes these kind of things a LOT easier.

From now on, I'm assuming the following folder structure:

project/
    assets/
        js/
        templates/

I'm also assuming you have node.js installed on your machine!


Installing Grunt

$ cd project/assets/
$ sudo npm install grunt --save-dev

Installing Handlebars Plugin

You'll also need the Handlebars Grunt plugin in order to precompile your templates:

Install it like this:

$ sudo npm install grunt-contrib-handlebars --save-dev

Gruntfile.js

Notes:

  • A Gruntfile.js is a configuration file for Grunt
  • It lives at the root of where is installed your Grunt CLI instance
  • It's written in plain javascript

Create the file:

$ touch Gruntfile.js

Then copy/paste this typical Gruntfile for accomplishing what you want to achieve:

module.exports = function(grunt) {

  /*
   * https://github.com/gruntjs/grunt/wiki/Configuring-tasks
   */
  grunt.initConfig({

    "handlebars": {
      compile: {
        options: {
          amd: true
        },
        src: ["templates/**/*.html"],
        dest: "js/precompiled.handlebars.js"
      }
    }

  });

  // Requires the needed plugin
  grunt.loadNpmTasks('grunt-contrib-handlebars');
};

All plugin options here.

Running the task

Then, assuming that you have templates living in assets/templates/, run:

$ grunt handlebars:compile

If everything is ok you should be able to see your compiled templates in js/precompiled.handlebars.js:

define([

    // Should be `handlebars.runtime.js` here
    // See: http://handlebarsjs.com/precompilation.html
    'handlebars'

], function(Handlebars) {

  this["JST"] = this["JST"] || {};

  this["JST"]["templates/template_one.html"] = Handlebars.template(function(Handlebars,depth0,helpers,partials,data) { /* ... */ });

  this["JST"]["templates/template_two.html"] = Handlebars.template(function(Handlebars,depth0,helpers,partials,data) { /* ... */ });

  //...

  return this["JST"];

});

Integration with RequireJS

Obviously, in your Views, you'll have to change your dependencies:

define([
  'marionette',
  //'handlebars', /* You don't need this _here_ anymore */
  'js/compiled.handlebars'

], function(Marionette, JST) {

    /* edited for brevity */

    MyApp.StaticView = Marionette.ItemView.extend({
        template: JST["templates/template_one.html"]
    });

    MyApp.on("initialize:after", function(){
        var staticView = new MyApp.StaticView();
        MyApp.mainRegion.show(staticView);  
    });
});
Tranquil answered 14/11, 2013 at 3:7 Comment(4)
I wish I could vote twice, Very Helpful! Is there any way I can simplify accessing the template instead of using the file relative path as the key?Spelling
I don't think so. But there may be an option in the Handlebars precompiler.Tranquil
I found an option called processName. handlebars: { compile: { options: { amd: true, processName: function(filePath) { var pieces = filePath.split("/"); return pieces[pieces.length - 1].split(".")[0]; } }, src: ['src/js/templates/*.handlebars'], dest: 'src/js/templates.js' } }Spelling
I can't get this to work... Can you maybe provide a gist with some examples? It keeps saying that in cannot call function 'template' of undefined (which should be Handlebars)Agency

© 2022 - 2024 — McMap. All rights reserved.