rahulthewall rahulthewall - 2 months ago 16
Node.js Question

Handlebars: Partial not found on first run

I have a node script to compile a template using handlebars. Here's my template:

<div class="header">
<h1>{{title}}</h1>
</div>
<div class="body">
<p>{{body}}</p>
</div>
<div class="footer">
<div><a href="http://twitter.com/{{author.twitter}}">{{autor.name}}</a>
</div>
<ul>
{{#each tags}}
<li>{{this}}</li>
{{/each}}
</ul>
{{> example_partial}}
</div>


The corresponding partial is

<div>
<p>
Hi, I am a partial!
</p>
</div>


And the JS

var handlebars = require('handlebars'),
fs = require('fs');

var data = {
title: 'practical node.js',
author: '@azat_co',
tags: ['express', 'node', 'javascript']
}
data.body = process.argv[2];

fs.readFile('handlebars-example-partial.html', 'utf-8', function(error, source) {
handlebars.registerPartial('example_partial', source);
});

fs.readFile('handlebars-example.html', 'utf-8', function(error, source){

var template = handlebars.compile(source);
var html = template(data);
console.log(html)
});


What I can't really figure out then when I run the script via node for the first time using
node app.js
, I get the following error:

/Users/rahul/stencil/node_modules/handlebars/dist/cjs/handlebars/runtime.js:266
throw new _exception2['default']('The partial ' + options.name + ' could not be found');
^
Error: The partial example_partial could not be found
at Object.invokePartial (/Users/rahul/stencil/node_modules/handlebars/dist/cjs/handlebars/runtime.js:266:11)
at Object.invokePartialWrapper [as invokePartial] (/Users/rahul/stencil/node_modules/handlebars/dist/cjs/handlebars/runtime.js:68:39)
at Object.eval (eval at createFunctionContext (/Users/rahul/stencil/node_modules/handlebars/dist/cjs/handlebars/compiler/javascript-compiler.js:254:23), <anonymous>:16:28)
at main (/Users/rahul/stencil/node_modules/handlebars/dist/cjs/handlebars/runtime.js:173:32)
at ret (/Users/rahul/stencil/node_modules/handlebars/dist/cjs/handlebars/runtime.js:176:12)
at ret (/Users/rahul/stencil/node_modules/handlebars/dist/cjs/handlebars/compiler/compiler.js:525:21)
at /Users/rahul/stencil/examples/standalone_v1/handlebars-example.js:29:14
at tryToString (fs.js:414:3)
at FSReqWrap.readFileAfterClose [as oncomplete] (fs.js:401:12)


However, when I run the program again, it works fine and I get the expected output (without changing anything). Could someone plese explain to me what I am doing wrong?

Answer

The problem is your partial isn't actually registered by the time you get to compiling the template that uses it. This is because fs.readFile is an asynchronous operation.

One solution would be to use fs.readFileSync:

var partial = fs.readFileSync('handlebars-example-partial.html', 'utf-8'); 
handlebars.registerPartial('example_partial', source);

fs.readFile('handlebars-example.html', 'utf-8', function(error, source){
  var template = handlebars.compile(source);                                                                                                                                                                                                 
  var html = template(data);                                                                                                                                                                                                                 
  console.log(html)
});

Or you could put it all in the callback of the registering of the partial:

fs.readFile('handlebars-example-partial.html', 'utf-8', function(error, partial) {
  handlebars.registerPartial('example_partial', partial);
  fs.readFile('handlebars-example.html', 'utf-8', function(error, template) {
    var compiled = handlebars.compile(template);
    var html = compiled(data);
    console.log(html);
  });
}