Handlebars.js: Error: Unknown template object: string when using a precompiled template with .template()

Created on 12 Dec 2014  ·  4Comments  ·  Source: handlebars-lang/handlebars.js

I'm trying to register a partial, which according to the docs should be done like this:

var partialString = fs.readFileSync(filename), {encoding: 'utf8'})
var compiledPartial = handlebars.precompile(partialString)
var template = handlebars.template(compiledPartial)

handlebars.registerPartial('stuff', template)

However the .template() method produces this error:

Error: Unknown template object: string

I can see that the compiledPartial above is indeed a string that looks like an object:

{"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
return "<div>Stuff</div>\n";
},"useData":true}

WTF?

Node 0.10.25, Handlebars 2.0.0

Most helpful comment

I am writing a server-side application that needs to be able to both precompile templates (and then deliver them to the client in case they need to be re-rendered with new context) and also render them to HTML (for less noticeable flicker/delay on the client + improved SEO). It seems I either need to call both precompile and compile separately or I need to eval the serialized precompiler output. Neither of those approaches feels very elegant, so am I missing something or is my application just unusual? (BTW, this application is written in Java but using the built-in [Rhino/Nashorn] JavaScript engine for the templating portion; hopefully that doesn't matter.)

In case anyone else got confused by the documentation for precompile and wound up here, I've written an example to illustrate what is going on. (Actually, I wrote it because I was trying to figure it out what I was doing wrong.)

var templateSources = {
    hi: "Hi, {{name}}.",
    bye: "Goodbye, {{name}}."
};
var compiledTemplates = {};
var serializedTemplates = {};
var deserializedTemplates = {};

Object.keys(templateSources).forEach(function(name) {
    // Use compile method to generate actual executable template (function)
    compiledTemplates[name] = Handlebars.compile(templateSources[name]);
    // Use precompile method to generate serialized JS (string)
    serializedTemplates[name] = Handlebars.precompile(templateSources[name]);
    // If we really want, we can deserialize these 
    deserializedTemplates[name] = Handlebars.template(evalPrecompiledTemplate(serializedTemplates[name]));
});

// (Yes, I know eval is dangerous)
function evalPrecompiledTemplate(s) {
    return eval("(function(){return " + s + "}());");
}

// Quick demonstration that these template functions work the same
var context = {
    name: "John Smith"
};

// Output: 
// Rendering template named hi with context: Object {name: "John Smith"}
// Hi, John Smith.(compiled)
// Hi, John Smith.(precompiled/deserialized)
// Rendering template named bye with context: Object {name: "John Smith"}
// Goodbye, John Smith.(compiled)
// Goodbye, John Smith.(precompiled/deserialized)
Object.keys(templateSources).forEach(function(name) {
  console.log("Rendering template named " + name + " with context:", context);
  console.log(compiledTemplates[name](context) + "(compiled)");
  console.log(deserializedTemplates[name](context) + "(precompiled/deserialized)");
});

All 4 comments

The precompiler method is intended for serializing the template and then the template method is used to restore it on the client. If you are trying to evaluate the template in the same process then you should use Handlebars.compile directly.

Thanks for the answer. It seems the docs are more geared towards using Handlebars in the client side, since it is possible to pass just the uncompiled partial string to the registerPartial() method, if you're only using Handlebars on the server side.

I am writing a server-side application that needs to be able to both precompile templates (and then deliver them to the client in case they need to be re-rendered with new context) and also render them to HTML (for less noticeable flicker/delay on the client + improved SEO). It seems I either need to call both precompile and compile separately or I need to eval the serialized precompiler output. Neither of those approaches feels very elegant, so am I missing something or is my application just unusual? (BTW, this application is written in Java but using the built-in [Rhino/Nashorn] JavaScript engine for the templating portion; hopefully that doesn't matter.)

In case anyone else got confused by the documentation for precompile and wound up here, I've written an example to illustrate what is going on. (Actually, I wrote it because I was trying to figure it out what I was doing wrong.)

var templateSources = {
    hi: "Hi, {{name}}.",
    bye: "Goodbye, {{name}}."
};
var compiledTemplates = {};
var serializedTemplates = {};
var deserializedTemplates = {};

Object.keys(templateSources).forEach(function(name) {
    // Use compile method to generate actual executable template (function)
    compiledTemplates[name] = Handlebars.compile(templateSources[name]);
    // Use precompile method to generate serialized JS (string)
    serializedTemplates[name] = Handlebars.precompile(templateSources[name]);
    // If we really want, we can deserialize these 
    deserializedTemplates[name] = Handlebars.template(evalPrecompiledTemplate(serializedTemplates[name]));
});

// (Yes, I know eval is dangerous)
function evalPrecompiledTemplate(s) {
    return eval("(function(){return " + s + "}());");
}

// Quick demonstration that these template functions work the same
var context = {
    name: "John Smith"
};

// Output: 
// Rendering template named hi with context: Object {name: "John Smith"}
// Hi, John Smith.(compiled)
// Hi, John Smith.(precompiled/deserialized)
// Rendering template named bye with context: Object {name: "John Smith"}
// Goodbye, John Smith.(compiled)
// Goodbye, John Smith.(precompiled/deserialized)
Object.keys(templateSources).forEach(function(name) {
  console.log("Rendering template named " + name + " with context:", context);
  console.log(compiledTemplates[name](context) + "(compiled)");
  console.log(deserializedTemplates[name](context) + "(precompiled/deserialized)");
});

@jacobq you'll want to call compile and precompile. The generated code for the two is very different and I wouldn't presume that trying to toString or similar will work with the compile output, etc.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

sontek picture sontek  ·  3Comments

DylanPiercey picture DylanPiercey  ·  7Comments

snimavat picture snimavat  ·  5Comments

ShintaroOkuda picture ShintaroOkuda  ·  7Comments

novwhisky picture novwhisky  ·  4Comments