Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Node.js with Handlebars.js on server and client

I have an app in Node.js using Expressjs and Handlebars as the template engine.

Expressjs uses layouts and then renders views. The layout (layout.hbs) looks like this:

<!doctype html>
<html lang="en">
    <head>
    </head>
  <body>
    {{{body}}}
  </body>
</html>

The {{{body}}} is replaced server-side, within node.js when you access a route. For example:

app.get('/', function(req, res){
   res.render('index'})
})

Will replace the {{{body}}} tag with the contents of index.hbs.

Now, on the client side I'm using Backbone.js and want to use Handlebars for the views controlled via Backbone. The problem is that because these pages are already rendered through Handlebars, when I attempt to use Handlebars within it (or Handlebars within Handlebars) it doesn't work. There are no errors, it simply just doesn't replace tags with data.

Has anyone encountered this before or have any idea a work around?

Thank you!

like image 374
dzm Avatar asked Apr 06 '12 00:04

dzm


People also ask

Can Handlebars be used with node js?

In this article, we covered the basics of Handlebars - a templating engine for Node. js and front-end JavaScript. Using Handlebars, we can create dynamic webpages that render on the server side or client side.

Are Handlebars server-side?

Pro Handlebars Handlebars is good for rendering in CLI-apps, non-HTML text content, server-side rendering of pure contents. Handlebars has been ported to many programming languages (Java, Rust etc).

Is node js both server-side and client side?

Node. js is a server-side JavaScript run-time environment. It's open-source, including Google's V8 engine, libuv for cross-platform compatibility, and a core library.

CAN node JS run on client side?

Being able to call Node. js modules from JavaScript running in the browser has many advantages because it allows you to use Node. js modules for client-side JavaScript applications without having to use a server with Node.


7 Answers

You should use pre-compiled client templates. They are faster executing and allow you to use the same template language on the server and client.

  1. Install handlebars globally npm install handlebars -g
  2. Precompile your templates handlebars client-template1.handlebars -f templates.js
  3. Include templates.js <script src="templates.js"></script>
  4. Execute the template var html = Handlebars.templates["client-template1"](context);

https://stackoverflow.com/a/13884587/8360

like image 164
Zachary Yates Avatar answered Oct 01 '22 12:10

Zachary Yates


An easy way to do this is to just append a \ before the {{ in a Handlebars file. For example:

<script type="text/x-template" id="todo-item-template">
<div class="todo-view">
    <input type="checkbox" class="todo-checkbox" \{{checked}}>
    <span class="todo-content" tabindex="0">\{{text}}</span>
</div>

<div class="todo-edit">
    <input type="text" class="todo-input" value="\{{text}}">
</div>

<a href="#" class="todo-remove" title="Remove this task">
    <span class="todo-remove-icon"></span>
</a>

The above code will be rendered on the client with the {{..}} tags preserved.

like image 28
Tilo Mitra Avatar answered Oct 01 '22 10:10

Tilo Mitra


Yup, it's a sticky problem --- kind of like the quoting problems in shell scripts which become a rats' nest of quoted quotes.

My solution is to use jade (a la haml) in expressjs (server-side) to output handlebars based templates for the client. This way, the server uses one syntax (jade), and the client uses another (handlebars). I am at the same crossroads as you, so I have the same challenge.

Of course, jade is not essential (though it's ready-made for expressjs). You could choose any (non-handlebars) template engine for the server, and/or you could use handlebars on the server with your non-handlebars templating on the client --- as long as the two syntaxes of your chosen templating engines do not collide. Since I'm using emberjs on the client and it uses handlebars syntax (by default), I prefer using emberjs + handlebars syntax on the client. So expressjs + jade became a natural fit for the server.

like image 44
occam Avatar answered Oct 01 '22 11:10

occam


Shameless self-promotion!

I wanted to do this same client/server sharing thing, so I wrote a little npm package to assist:

node-handlebars-precompiler

I whipped it up in a couple hours based on the command-line compiler in wycats' handlebars repo. It's not the greatest code in the world, but it's been getting the job done for me very well.

EDIT: I am no longer maintaining this package. If you would like to take over, please contact me via Github. I mainly use Jade templates now, so it doesn't make sense for me to continue as the maintainer.

like image 24
Joel Wietelmann Avatar answered Oct 01 '22 11:10

Joel Wietelmann


I have worked around this by passing client-side templates through server-side templates.

So on the server-side read all your client-side templates to an array and pass it to your render function on the server-side

In your route handler do something like:

readTemplates(function(err, clientTemplates) {
  res.render("page", {
    clientTemplates: clientTemplates;   
  });
});

And then in layout.hbs:

{{#each clientTemplates}}
<script type="text/handlebars id="{{this.filename}}" >
{{{this.template}}}
</script>
{{/each}}

Here I'm using file names without extensions as the template id so that they can be referenced from Backbone views. Oh, and remember to implement caching for production mode.

Yeah, this sucks.

I think we should write a Handlebars/Express/Connect helper for this.

like image 34
Epeli Avatar answered Oct 01 '22 12:10

Epeli


I didn't like the precompilation solution (because I want to define templates in the same file where I will use them) nor the naive \{{ escape solution (because it needs the full Handlebars compiler and more javascript code) so I came up with an hybrid solution that uses Handlebars' helpers:

1) Register a new helper called "template" on server configuration

var hbs = require('hbs');
hbs.registerHelper("template", function(key, options){
    var source = options.fn().replace("\\{{", "{{");
    var ret =
    '<script>\n' + 
        key + ' = function(opt){\n' +
            'return Handlebars.template(' + hbs.handlebars.precompile(source) + ')(opt);\n' +
        '}\n' + 
    '</script>';
    return ret;
});


2) Use it anywhere in your client-side webpage (with \{{ escape for client-side parameters)

{{#template "myTemplate"}}
    <div>
        <p>Hello \{{this.name}}!</p>
    </div>
{{/template}}

(the server will precompile it in something like this)

<script>
    myTemplate = function(opt){
        return Handlebars.template(/* HBS PRECOMPILATED FUNCTION */)(opt);
    }
</script>


3) Simply call the function where you need it in client-side javascript

var generatedHtml = myTemplate("world");   // = <div><p>Hello world!</p></div>
$("#myDiv").html(generatedHtml);           // or whatever
like image 45
Oneiros Avatar answered Oct 01 '22 10:10

Oneiros


You have 2 options. The second is THE best way to go:

1) Escape the mustaches

<script type="text/x-handlebars" data-hbs="example">
  <p>\{{name}}</p>
</script>

2) Precompile

This will compile the template on the server before it goes to the client. This will make the template ready to use and reduces the burden on the browser.

like image 30
jordanb Avatar answered Oct 01 '22 12:10

jordanb