Search code examples
javascripttemplatesgulphandlebars.jsprecompiled-templates

Handlebars: TypeError: Cannot read property 'helperMissing' of undefined


I am having a problem with Handlebars compiled templates. I feel like I am completely missing something important, but try as I might, I can't seem to work it out, or find any information online as to why this particular situation is presenting itself.

I am compiling using a Gulp task using gulp-handlebars. The gulp task is:

gulp.task('build-hbs', function(){
  gulp.src('root/app/src/hbs/*.hbs')
  .pipe(handlebars())
  .on('error', notify.onError({
    message: 'Error: <%= error.message %>'
  }))
  .pipe(declare({
    namespace: 'Handlebars.templates',
    noRedeclare: true // Avoid duplicate declarations
  }))
  .pipe(concat('templates.js'))
  .pipe(gulp.dest('root/app/js/templates/'));
});

for simple templates things are working OK, however I am now trying to use a simple helper (Note: when working with non-compiled templates, the helper works fine). The helper is:

Handlebars.registerHelper('gravatar', function(hash, size) {
  var grav = '<img src="http://www.gravatar.com/avatar/' + hash + '.jpg?r=g&d=mm&s=' + size + '">';
  return new Handlebars.SafeString(grav);
});

The template itself looks like this:

<div id="nick"><b>{{display}}</b></div>
<div id="image">{{gravatar hash}}</div>

Once compiled, I get:

this["Handlebars"] = this["Handlebars"] || {};
this["Handlebars"]["templates"] = this["Handlebars"]["templates"] || {};
this["Handlebars"]["templates"]["profile"] = {"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
    var helper, alias1=helpers.helperMissing, alias2="function", alias3=this.escapeExpression;

  return "<div id=\"nick\">\r\n  <b>"
    + alias3(((helper = (helper = helpers.display || (depth0 != null ? depth0.display : depth0)) != null ? helper : alias1),(typeof helper === alias2 ? helper.call(depth0,{"name":"display","hash":{},"data":data}) : helper)))
    + "</b>\r\n</div>\r\n<div id=\"image\">\r\n  <img src=\"http://www.gravatar.com/avatar/"
    + alias3(((helper = (helper = helpers.hash || (depth0 != null ? depth0.hash : depth0)) != null ? helper : alias1),(typeof helper === alias2 ? helper.call(depth0,{"name":"hash","hash":{},"data":data}) : helper)))
    + "?s=32\">\r\n</div>";
},"useData":true};

Within my JS code, I do something like:

$('#profile').html(Handlebars.templates.profile({
    display:'user 1',
    hash:'abdcef....'
}));

When I run the code I get the error:

TypeError: Cannot read property 'helperMissing' of undefined

Which relates to the compiled template code:

...
var helper, alias1=helpers.helperMissing, alias2="function", alias3=this.escapeExpression;
...

I can't seem to find any reason for this code to be added, or any reference to the helperMissing function in the Handlebars documentation...

Any insights in to why this might be happening would be extremely welcome!


Solution

  • In the end, the problem turned out to be one of conflicting versions!

    The way I ended up fixing it was to change the gulp file a little:

    gulp.task('build-hbs', function(){
      gulp.src('root/app/src/hbs/*.hbs')
        .pipe(handlebars({
          handlebars: require('handlebars')
        }))
        .pipe(wrap('Handlebars.template(<%= contents %>)'))
        .pipe(declare({
          namespace: 'Handlebars.templates',
          noRedeclare: true // Avoid duplicate declarations
        }))
        .pipe(insert.prepend('var Handlebars = require("handlebars");\n'))
        .pipe(concat('templates.js'))
        .pipe(gulp.dest('root/app/js/templates/'));
    });
    
    gulp.task('copy', function(){
      gulp.src('node_modules/handlebars/dist/handlebars.runtime.js')
        .pipe(gulp.dest('root/app/js/libs/'));
    });
    

    The main difference is that is specifically loads the version of handlebars installed by npm to use as the compiler.

    The second job copies the handlebars.runtime.js file from the node_modules folder in to the folder where the browser will actually pick it up. This guarantees compatibility!

    The wrap and declare calls are required to make sure the compiled code is actually correct (which is different from the information on the handlebars site - the gulp-handlebars module just works in a slightly odd way).

    Finally the insert adds a require call to make sure it works in a stand-alone manner to ensure the Handlebars dependency is met at runtime.

    Finally, there was an error in my template which should have read:

    <div id="nick"><b>{{display}}</b></div>
    <div id="image">{{gravatar hash 48}}</div>
    

    the missing second parameter to the gravatar helper caused an error - this only presented itself once the compiled templates were working but was easy to spot at this point!