5

How to manually use the precompiled handlebars.js templates?

Let's say, we have

source = "<p>Hello, my name is {{name}}</p>"
data = { name: "Joe" }

Currently, I have

template = Handlebars.compile(source)
render: -> template(data)

The source is coming from the database, and in order to cut down on the compilation time, I want to use a compilation step, precompiling the template server side with Handlebars.precompile(source) and then using something like:

template = precompiled_template
render: -> precompiled_template(data)

The precompiled_template is a string with function definition, so that doesn't work.

Also, I've found that Hanlebars.compile(source)() == Handlebars.precompile(source), but after browsing the source codes of handlebars, it's compilers and runtime, I'm still not sure how to achieve this.

Greg Funtusov
  • 1,377
  • 15
  • 18
  • Have you looked at how [handlebars_assets](https://github.com/leshill/handlebars_assets) works? There's probably a bunch of Rails noise in there but maybe it'll offer some insight anyway. – mu is too short Jan 02 '13 at 21:42
  • Thanks for your input; I checked it, but it presents a Sprockets engine that delegates to something else and in the end I didn't track it down either. However, I found the answer, it uses eval and is, overall, not so beautiful, but at least it works and seems to give a performance boost. – Greg Funtusov Jan 03 '13 at 22:31
  • Kinda curious, here, why neither of the answers are marked as an answer... Daniel seems to have put a lot of work into his answer, and it would be nice to see him rewarded for it; it also makes it easier when another user (like myself, for example) is looking for an answer to actually have one marked as accepted. – Steve Dec 02 '13 at 23:13
  • @Steve see my comment to that answer. Basically, I needed to store the compiled template in database (and retrieve it from there) and interacting with that was the hard part for me (and the question). Daniel's answer is definitely good, but I cannot say that it answers the question, which I mentioned in my reply. I believe this is a useful scenario for SaaS's with customisable templates, so I left this question open, hoping someone might have a solution better than evaling the template from the database. – Greg Funtusov Dec 15 '13 at 06:47

2 Answers2

4

if you did not find the right question till now, the answer is pretty simple. Handlebars comes with a C pre-compiler on the command line, if you can access your shell you can simple just compile your templates each separated or merge them together into one file.

you can install Handlebars via npm / or build it on your system. on the shell you can access the help file

$> Handlebars [ENTER]

You will see a help file like > - f --output Output File etc etc .. - m --min Minimize Output

$> Handlebars mysupertemplate.handlebars -f compiled.js -m ("-m" if you want to minify the js file)

To run Handlebars.compile in the browser is a huge loss in performance, so it's worth a try to precompile on the server before sending the file to the browser.

To register Handlebars templates in your browser you have to load them like this:

var obj = {"foo":"bar"}

var template = require("./mytemplate-file.js") // require.js example
    template = template(Handlebars) // Pass Handlebars Only if the template goes mad asking for his Father

var html = Handlebars.templates[template-name](obj)

For example if you have more then one template registered in the "templates-file" you will be able to access after the require call all templates by name using

var html = Handlebars.templates["templateName"]({"foo":"bar"});

You can go even further by register all the know helper within the file and / or making custom helpers for partials like so..

*// This will be the helper in you app.js file*

Handlebars.registerHelper("mypartials", function(partialName, data) {
     var partial = Handlebars.registerPartial(partialName) || data.fn
     Handlebars.partials[partialName] = partial
})

And in your template file you can put this...

{{#mypartial "divPartial"}}
  <div id="block"><h2>{{foo}}</h2><p>{{bar}}</p></div>  
{{/mypartial}}

{{#mypartial "formPartial"}}
  <form id="foo"><input type="text" value="{{foo}}" name="{{bar}}"></form>
{{/mypartial}}

Now you can access this files by calling

var html = Handlebars.partials["divPartial"]({"foo":"bar","bar":"foo"})
var formHtml = Handlebars.partials["formPartial"]({"bar":"bar","foo":"foo"})

Hope this helped a bit ..

Daniel
  • 111
  • 4
  • Hey Daniel, thanks for the reply! I agree, precompiling is simple in the basic case, however I will have user uploaded templates in the database. I was trying to use server-side javascript to precompile the templates on the server and then store it and use afterwards. – Greg Funtusov Feb 21 '13 at 03:02
  • I don't know what kind of platform you use, all my samples are node based, and for creating a js file (even if it's a temporary) i wrote a small cli that executes argv arguments and based on that the file will be compiled. actually it doesn't matter if you use php etc, you can use node for writting new files as js, json or whatever. With the REPL and FS modules from node it should be possible to accomplish something you are looking for.. – Daniel Feb 21 '13 at 05:03
0

This speed test http://jsperf.com/handlebars-compile-vs-precompile/3 gave the answer.

Apparently, one solution is to eval() that resulting string and it will work.

The code is

var data = { name: "Greg" };
var source = "<p>Howdy, {{ name }}</p>";

eval("var templateFunction = " + Handlebars.precompile(source));
var template = Handlebars.template(templateFunction);

template(data);
=> "<p>Howdy, Greg</p>" 

Of course one needs to be careful with eval and probably a better solution exists.

Greg Funtusov
  • 1,377
  • 15
  • 18
  • 1
    You could also, presumably, write the strings to a JavaScript file containing a big object literal which maps template names to their functions. Then pull that JavaScript over to the client. – mu is too short Jan 03 '13 at 23:12
  • 1
    Really? Suggesting someone use `eval()` is really not a good idea pretty much ever. http://stackoverflow.com/questions/86513/why-is-using-the-javascript-eval-function-a-bad-idea – pseudosavant Jan 31 '13 at 00:15
  • 1
    Pseudosavant, any alternatives you'd like to share? – Greg Funtusov Feb 02 '13 at 20:50
  • 3
    If you *really* need to execute a string as code then `(new Function(strCode))()` is more performant. This link (http://moduscreate.com/javascript-performance-tips-tricks/) from the SO post I linked to already has details on it. Also, MDN has more details on why you shouldn't use `eval()` and alternatives. https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/eval#Don.27t_use_eval.21 – pseudosavant Feb 13 '13 at 18:52
  • Thanks for the new Function approach, that definitely helps! eval() is definitely evil, but re: executing string as code – I outlined the usecase in the question. If there's a better way, without executing the code – it would be great if someone can share it. I posted the method that was working for me, but didn't mark the question as accepted in hope that something better will appear :) – Greg Funtusov Feb 14 '13 at 01:10