Help with helper syntax
dotherightthing opened this issue · 50 comments
I'm using the latest version of grunt-compile-handlebars
(^2.0.0
).
I've been using the following helper pattern successfully with kss-node:
module.exports = function(handlebars) {
handlebars.registerHelper('zeroOffset', function(_index) {
return new handlebars.SafeString( parseInt(_index, 10) + 1 );
});
};
grunt-compile-handlebars is loading my helper script via the helpers
option. I know this is working because if I change the filename I get an error:
Warning: ENOENT, no such file or directory
I use the helper like this:
{{#each myLoop}}
<p>{{zeroOffset @index}}</p>
{{/each}}
However when I use it with grunt-compile-handlebars. I get:
Warning: Missing helper: "zeroOffset" Use --force to continue.
If I change the syntax to match the patterns used on handlebarsjs.com, eg:
Handlebars.registerHelper('zeroOffset', function(context, options) {..}
I get:
Warning: Handlebars is not defined Use --force to continue.
I've checked out your test super_helper.js
but it doesn't use a name, so I can't call it the way I want to.
Any pointers you could give me would be really helpful thanks.
Cheers,
Dan
hey dan!
the name of each helper is based off of its file name. The super_helper
you noticed in our repo is available as {{> super helper}}
.
So you are actually really close in your example. You would just need to change this
module.exports = function(handlebars) {
handlebars.registerHelper('zeroOffset', function(_index) {
return new handlebars.SafeString( parseInt(_index, 10) + 1 );
});
};
to this
module.exports = function(_index) {
return new handlebars.SafeString( parseInt(_index, 10) + 1 );
};
and name that file zeroOffest
Hi Patrick,
Thanks for your super-fast reply!
I changed the syntax as suggested but the helper still isn't being recognised:
// zeroOffset.js
module.exports = function(_index) {
return new handlebars.SafeString( parseInt(_index, 10) + 1 );
};
// Gruntfile.js
'compile-handlebars': {
modules: {
files: [{
expand: true,
cwd: '<%= project.dev.modules %>/',
src: [
'**/*.hbs',
'!**/_*.hbs',
'!**/OLD_*.hbs' // prototypes
],
dest: '<%= project.build.html_compiled_includes %>/',
ext: '.html'
}],
partials: ['<%= project.dev.modules %>/**/_*.hbs'],
helpers: '<%= project.dev.helpers %>/handlebars/zeroOffset.js',
registerFullPath: true,
templateData: '<%= project.dev.modules %>/**/*.json'
}
Is there anything else that is obviously amiss here?
Thanks,
Dan
use an array for helpers, rather just a string
Thanks, but same deal with an array:
helpers: ['<%= project.dev.helpers %>/handlebars/zeroOffset.js'],
hmm... whats the exact error you are getting now?
Same error:
Warning: Missing helper: "zeroOffset" Use --force to continue.
Would you be able to place some logging here to get the path to the helpers being registered?
console.log( name ); // resources/dev/helpers/handlebars/zeroOffset
console.log( helper ); // resources/dev/helpers/handlebars/zeroOffset.js
console.log( fs.realpathSync(helper) ); // /Users/Dan/Websites/outwardbound-2015/wp-content/themes/outwardbound2014/resources/dev/helpers/handlebars/zeroOffset.js
is /Users/Dan/Websites/outwardbound-2015/wp-content/themes/outwardbound2014/resources/dev/helpers/handlebars/zeroOffset.js
the correct file?
Yes, here is the full file:
nano /Users/Dan/Websites/outwardbound-2015/wp-content/themes/outwardbound2014/resources/dev/helpers/handlebars/zeroOffset.js
/** @method
* @name zeroOffset
*
* @author dan.smith@chrometoaster.com
*
* @summary Used with each loops to offset the zero based numbering (i.e. 0 -> 1)
*
* @param {Number} index - The loop index
*/
// kss-node syntax:
/*
module.exports = function(handlebars) {
handlebars.registerHelper('zeroOffset', function(_index) {
return new handlebars.SafeString( parseInt(_index, 10) + 1 );
});
};
*/
// grunt-compile-handlebars syntax
module.exports = function(_index) {
return new handlebars.SafeString( parseInt(_index, 10) + 1 );
};
It looks to me like compile-handlebars.js is receiving a path where it expects a name
.
Could this be a side effect of the registerFullPath
option?
are you exposing two modules through module.export
? If that is the case, thats invalid. Try require
ing the file in a node repl and you should see it results in just an empty object being returned
Sorry can you repeat in laymans terms? What's a node repl? I'm only including this helper once, via the helpers option
. It's not used with kss-node, only with grunt-compile-handlebars.
Also FYI, if I do the following I get the handlebars is not defined
error:
var name_unpathed = name.substr( name.lastIndexOf('/') + 1 );
handlebars.registerHelper(name_unpathed, require(fs.realpathSync(helper)));
console.log( name_unpathed );
console.log( helper );
console.log( fs.realpathSync(helper) );
sorry, didn't mean to use buzzwords :[
the repl (or Read-Eval-Print-Loop) is just the program that opens up when you run node
by itself in the command line. This is really similar to the console in a browser, in that you type in commands and they get immediately executed and returned.
What I was suggesting is opening up node
and then running var zeroOffset = require('/Users/Dan/Websites/outwardbound-2015/wp-content/themes/outwardbound2014/resources/dev/helpers/handlebars/zeroOffset.js')
then checking to see what zeroOffset
is. If the module is valid, you will get a function. If its invalid it will be {}
or undefined
you are getting the handlebars is not defined
error because handlebars is not defined :]
you will need to require
handlebars in your helper in order to have access to it. Its not a global
Ok thanks for clarifying.
I'm not sure if I should require
the bin executable or the JS file -
Attempt 1:
var handlebars = require('../../../../node_modules/grunt-compile-handlebars/node_modules/handlebars/dist/handlebars.js');
module.exports = function(_index) {
return new handlebars.SafeString( parseInt(_index, 10) + 1 );
};
Output 1:
undefined
Attempt 2:
var handlebars = require('../../../../node_modules/grunt-compile-handlebars/node_modules/handlebars/bin/handlebars');
module.exports = function(_index) {
return new handlebars.SafeString( parseInt(_index, 10) + 1 );
};
Output 2:
var zeroOffset = require('/Users/Dan/Websites/outwardbound-2015/wp-content/themes/outwardbound2014/resources/dev/helpers/handlebars/zeroOffset.js')
Precompile handlebar templates.
Usage: node [template|directory]...
Options:
-f, --output Output File
--map Source Map File [string] [default: undefined]
-a, --amd Exports amd style (require.js)
-c, --commonjs Exports CommonJS style, path to Handlebars module [default: null]
-h, --handlebarPath Path to handlebar.js (only valid for amd-style) [default: ""]
-k, --known Known helpers
-o, --knownOnly Known helpers only
-m, --min Minimize output
-n, --namespace Template namespace [default: "Handlebars.templates"]
-s, --simple Output template function only.
-r, --root Template root. Base value that will be stripped from template names.
-p, --partial Compiling a partial template
-d, --data Include data when compiling
-e, --extension Template extension. [default: "handlebars"]
-b, --bom Removes the BOM (Byte Order Mark) from the beginning of the templates.
-v, --version Prints the current compiler version
--help Outputs this message
from within zeroOffset.js
, add var handlebars = require('handlebars')
Yes, so I tried that first (sorry didn't mention it), and I got undefined
that would mean you haven't installed it.
npm install handlebars --save
But why can't I use your copy?
Ok,
npm install handlebars --save
node
var zeroOffset = require('/Users/Dan/Websites/outwardbound-2015/wp-content/themes/outwardbound2014/resources/dev/helpers/handlebars/zeroOffset.js')
Returns
undefined
you could, but its in a different module scope. You would need to use a relative path from that file to the node_modules/grunt-compile-handlebars/node_modules/handlebars
bits are pretty much free, so if it was me, I would just add handlebars as a dep than have ugly code.
yeah, any variable assigned will return undefined
. What is zeroOffset equal to?
zeroOffset = the function in the zeroOffset.js file?
var handlebars = require('handlebars');
module.exports = function(_index) {
return new handlebars.SafeString( parseInt(_index, 10) + 1 );
};
So if no _index
argument is passed in I suppose it will be undefined
?
so its working?
I am not sure - thats a handlebars question more than anything
Ah no, after all this I still get
Warning: Missing helper: "zeroOffset" Use --force to continue.
However if I implement my earlier hotfix into compile-handlebars.js, it does work:
var name_unpathed = name.substr( name.lastIndexOf('/') + 1 );
handlebars.registerHelper(name_unpathed, require(fs.realpathSync(helper)));
im confused as to what the issue is because we have gone back and forth so much.
could you resumarize?
Sure, to summarise:
I have a Grunt compile-handlebars:modules
task which I run on .hbs
files:
'compile-handlebars': {
modules: {
files: [{
expand: true,
cwd: '<%= project.dev.modules %>/',
src: [
'**/*.hbs',
'!**/_*.hbs',
'!**/OLD_*.hbs' // prototypes
],
dest: '<%= project.build.html_compiled_includes %>/',
ext: '.html'
}],
partials: ['<%= project.dev.modules %>/**/_*.hbs'],
helpers: ['<%= project.dev.helpers %>/handlebars/zeroOffset.js'],
registerFullPath: true,
templateData: '<%= project.dev.modules %>/**/*.json'
}
},
The zeroOffset.js helper is used like so in .hbs
files, to output 1,2,3..
rather than 0,1,2...
:
{{#each someLoop}}
{{zeroOffset @index}}
{{/each}}
zeroOffset.js contains the following:
var handlebars = require('handlebars'); // we installed and referenced this to fix an error
module.exports = function(_index) {
return new handlebars.SafeString( parseInt(_index, 10) + 1 );
};
When I run grunt compile-handlebars:modules
I get:
Warning: Missing helper: "zeroOffset" Use --force to continue.
You suggested adding logging to compile-handlebars.js.
I noted that the name
passed to handlebars.registerHelper
was actually the relative path to the helper file:
handlebars.registerHelper(name, require(fs.realpathSync(helper))); // line 203
console.log( name ); // resources/dev/helpers/handlebars/zeroOffset
console.log( helper ); // resources/dev/helpers/handlebars/zeroOffset.js
console.log( fs.realpathSync(helper) ); // /Users/Dan/Websites/outwardbound-2015/wp-content/themes/outwardbound2014/resources/dev/helpers/handlebars/zeroOffset.js
I stripped the path off the name and it now works:
var name_unpathed = name.substr( name.lastIndexOf('/') + 1 );
handlebars.registerHelper(name_unpathed, require(fs.realpathSync(helper))); // line 203
console.log( name ); // zeroOffset
console.log( helper ); // resources/dev/helpers/handlebars/zeroOffset.js
console.log( fs.realpathSync(helper) ); // /Users/Dan/Websites/outwardbound-2015/wp-content/themes/outwardbound2014/resources/dev/helpers/handlebars/zeroOffset.js
@patrickkettner Hi, could you please publish this hotfix to npm? I could really use it, thanks.
So what should be a helper syntax ?
I'm facing this too: Warning: Missing helper: "..." Use --force to continue.
@patrickkettner I just got confused with the dates that were referenced in the discussion above, so I thought that the hotfix from @dotherightthing was not published yet. Then I got all I needed to work as I wanted and forgot to comment on that.
TL;DR: All is well, thanks for this grunt plugin!
@patrickkettner
Gruntfile.js
'compile-handlebars': {
globalJsonGlobbedTemplate: {
files: [{
expand: true,
cwd: './fixtures/',
src: './*.handlebars',
dest: './',
ext: '.html'
}],
templateData: './fixtures/data.json',
helpers: './fixtures/helpers/*.js',
}
}
}
./fixtures/helpers/helper.js
var Handlebars = require("handlebars");
module.exports = function() {
return new Handlebars.registerHelper("checkStatus", function (status) {
});
};
./fixtures/template.handlebars
<div>{{checkStatus}}</div>
Warning: Missing helper: "checkStatus" Use --force to continue.
@aakrem your helper file should have the same name as your helper.
./fixtures/helpers/checkStatus.js
instead of ./fixtures/helpers/helper.js
thanks
Need help with handlebars helper.
Helper
var Handlebars = require('handlebars');
module.exports = function() {
Handlebars.registerHelper("ifCond", function (v1, operator, v2, options) {
switch (operator) {
case '==':
return (v1 == v2) ? options.fn(this) : options.inverse(this);
case '===':
return (v1 === v2) ? options.fn(this) : options.inverse(this);
case '<':
return (v1 < v2) ? options.fn(this) : options.inverse(this);
case '<=':
return (v1 <= v2) ? options.fn(this) : options.inverse(this);
case '>':
return (v1 > v2) ? options.fn(this) : options.inverse(this);
case '>=':
return (v1 >= v2) ? options.fn(this) : options.inverse(this);
case '&&':
return (v1 && v2) ? options.fn(this) : options.inverse(this);
case '||':
return (v1 || v2) ? options.fn(this) : options.inverse(this);
default:
return options.inverse(this);
}
});
}
Gruntfile.js
"compile-handlebars": {
tpl: {
files: [{
src: "<%= path.tpl %>/tpl.hbs",
dest: "<%= path.root %>/<%= path.build.tpl %>/build.html"
}],
templateData: "<%= path.json %>/test.json",
helpers: "grunt/handlebars-helpers/ifCond.js"
}
}
Template
{{#test}}
{{#ifCond @index '==' 4}}
<div>{{@index}}</div>
{{/ifCond}}
{{/test}}
This code do nothing. What i do wrong?
what is the #test
helper?
#test
is json object:
{
"test": {
"el0": "...",
"el1": "...",
...
},
...
}
if skip custom helper this code compiled fine:
{{#test}}
<div>{{@index}}</div>
{{/test}}
The issue with your helper is that you appear to be trying to access the index from outside of a loop
you want something like this
{
array: ['a', 'b', 'c', 'd', 'e', 'f']
}
with the helper you already supplied
I get <div>4</div>
as output
try it out at tryhandlebars if you are trying to troubleshoot
Hi, I've installed the node module but I don't see the hotfix and I am getting the "helper not found' message. Any thoughts?
@dcilibiu there has never been a hotfix. @dotherightthing pushed a change to his own branch and there hasn't been a PR for it
I'm happy to help, but I would need a lot more information. How is your project setup? what are the folders and file structure, what does your helper look like, etc
For now it's good, I've added the helper in the root of the proj and it is working. Now it is not searching for the path only for the file (helpers: ['equal.js'])
I too can't get this to work.
Gruntfile.js
"compile-handlebars": {
static: {
files: [{
expand: true,
cwd: 'src/views/pages/',
src: '**/*.hbs',
dest: 'dist/',
ext: '.html'
}],
partials: 'src/views/partials/*.hbs',
helpers: ['src/views/helpers/inc.js']
}
}
src/views/helpers/inc.js
module.exports = function() {
return new Handlebars.registerHelper("checkStatus", function (value) {
return parseInt(value) + 1;
});
};
view.hbs
<h2 class="heading-small participant__header">
{{inc @index}}.
{{name}}
</h2>
Error: Uncaught Error: Missing helper: "inc"
@olefrank the name of the helper comes from the registerHelper
argument, not the filename. So you will want to be using checkStatus
Thanks Patrick. Now I don't get the "missing helper" error :)
But the helper is still not working. I tried several variations but nothing works.
I need a helper that adds 1 to the index (zero based). Like this:
- item x
- item y
- item z
This is what I tried so far. Nothing works. What am I missing??
module.exports = function() {
return new Handlebars.registerHelper("inc", function (value) {
return parseInt(value, 10) + 1;
});
};
module.exports = function(value) {
return new parseInt(value, 10) + 1 );
};
Handlebars.registerHelper("inc", function (value) {
return parseInt(value, 10) + 1;
});
@olefrank can you post the full code somewhere? The config, the package.json, the helper, everything. happy to debug, but I need to see a larger picture