I am precompiling my templates into JS using gulp-handlebars but I am having trouble getting custom handlebars helpers to also precompile. Does anyone know if there is any support/way to precompile custom helper methods?
I noticed that gulp-handlebars can use a specific handlebars library, essentially overriding its default. So, by just creating my own module and registering some helpers, and feeding that into the handlebars call, things are working locally.
// helpers.js
var handlebars = require('handlebars');
handlebars.registerHelper('upper', function(str){
return str.toUpperCase();
});
module.exports = handlebars;
And then in the gulpfile (something like this):
var handlebars = require('./src/js/helpers.js');
gulp.task('handlebars', function(){
gulp.src('src/html/*.html')
.pipe(gulp_handlebars({handlebars: handlebars})) // override library here
});
If you're using Browserify and optionally watchify and need the output to be a commonjs style module, gulp-defineModule will use a require('Handlebars') in the compiled template file.
This negates any registered helpers that you had passed into gulp-handlebars custom library option (see above). Here is an example of the output file we don't want:
// template.js
var Handlebars = require("handlebars");
module.exports = Handlebars.template({"compiler":[7,">= 4.0.0"]...
1: To fix this, create a helpers.js file that requires the handlebars library, adds the helpers, and then exports the library. Use gulp-defineModule's require option to pass in the handlebars library with the helpers like so:
.pipe(defineModule('node', {
require: {
Handlebars: '../helpers'
}
})
)
this will produce:
// template.js
var Handlebars = require("../helpers");
module.exports = Handlebars.template({...
Note that the relative path will be from the outputfile, and be careful on prod where filepaths may change.
2: Another way is to use gulp-wrap to define the module exactly how you want it. Something like:
.pipe(wrap('module.exports = function(Handlebars) {return Handlebars.template(<%= contents %>) }'))
then in main-js:
var Handlebars = require('./helpers');
var template = require('./template)(Handlebars);
Related
I'm using the updated documentation for Assemble.js and am having a hard time getting a clear picture of how to use Partial includes within subfolders using Handlebars.
The only meaningful reference I've found to partial config in assemblefile.js is in the deprecated gulp-assemble doc site.
Assemble.io isn't helpful since it's not up-to-date with the latest github repo.
Here's my assemblefile.js:
'use strict';
var assemble = require('assemble');
var app = assemble();
var streamRename = require('stream-rename');
var markdown = require('helper-markdown');
app.helper('markdown', require('helper-markdown'));
app.task('load', function(callback) {
app.partials(['templates/partials/*.hbs']);
app.pages(['templates/*.hbs']);
callback();
})
app.task('assemble', ['load'], function() {
return app.toStream('pages')
.pipe(app.renderFile())
.pipe(streamRename({extname:'.html'}))
.pipe(app.dest("./"));
});
app.task('default', ['assemble']);
module.exports = app;
My file structure:
root
---/templates
------index.hbs
------/partials
---------myPartial.hbs
When I try to invoke the partial from index.hbs with:
{{> partials/myPartial.hbs}}
The assemble task generates this error:
The partial partials/myPartial.hbs could not be found
How do I include Handlebars partials within subdirectories with a simple Assemble build?
Just use the stem of the partial filename:
{{> myPartial }}
There's also a built-in helper called partial that attempts to find the partial by the key (like myPartial) or other properties like path (partials/myPartial.hbs)
{{partial 'myPartial'}}
More information about how the templates work together can be found in the templates readme. These docs are also linked to from the assemble readme.
I'm wondering how helper dependencies are managed in compiled dust templates, specifically relating to being used on the client -- is the helper method bundled with the compiled dust template? What about dependencies that might not be supported on the client? Or if that dependency has multiple other dependencies?
Here's a trivial example of a dust template I'd like to be able to use on the client:
// foo.dust
{#myHelper}
<div>{foo}{bar}</div>
{/myHelper}
// my-helper.js
const isomorphicDep = require('isomorphic-dep');
const nodeDep = require('node-dep');
module.exports = function(dust) {
dust.helpers.myHelper = function(chunk, context, bodies, params) {
// do some stuff using deps
let foo = nodeDep.getFoo();
let bar = isomorphicDep.getBar(params.someInput);
return chunk.render(bodies.block, context.push({ foo, bar });
};
};
Thanks
A compiled template just contains instructions for how to render-- it does not include any client code itself.
For example, a simple template like this:
{#helper}foo{/helper}
Compiles into these two instruction sets:
function body_0(chk, ctx) {
return chk.h("helper", ctx, {
"block": body_1
}, {}, "h");
}
function body_1(chk, ctx) {
return chk.w("foo");
}
When the template is rendered, it asks Dust to look for the helper named helper and execute it (in the body_0 function). The code for helper is not included with the template.
So on the client, you'll need to include a file containing the helper that loads the correct isomorphic dep (like node-fetch vs whatwg-fetch, for example).
I have 2 model files containing a constructor in each, and an index.js file, that I wish to use to insert elements into a HTML file, using innerHTML. I want to use one of the variables from the js model file, however when I try to require the files in the index.js file, the innerHTML file suddenly stops working. Please note, the code in the current `window.onload' function is inserting h1 elements as a test, I will be replacing this with a return value from the constructor, but at the moment, when I require the files, even the h1 insert stops working. Code snippets that I think are relevant can be seen below:
index.js file:
var ToDo = require('../src/toDo.js');
var ToDoList = require('../src/toDoList.js');
window.onload = function() {
// create a couple of elements in an otherwise empty HTML page
var heading = document.createElement("h1");
var heading_text = document.createTextNode("Big Head!");
heading.appendChild(heading_text);
document.body.appendChild(heading);
}
Model file 1:
function ToDo(task) {
this.task = task;
this.complete = false;
}
module.exports = ToDo;
function ToDoList() {
this.array = [];
}
ToDoList.prototype.add = function(task) {
this.array.push(task);
};
ToDoList.prototype.popTask = function() {
var poppedTask = this.array.pop();
var concat = "<ul><li>";
var concat2 = "</li></ul>";
return (concat + poppedTask.task + concat2);
};
module.exports = ToDoList;
require is CommonJs feature, and it's not supported by browsers without this library. So you would need to use it in your project if you want modules with require syntax.
Update
To use require feature, you would need some module loader. There are two very popular that you could check out - Webpack or Browserify. They both support CommonJS require that you are looking for. As for me, I like webpack most, cause it is very powerful out of the box.
To read about their comparison :
https://medium.com/#housecor/browserify-vs-webpack-b3d7ca08a0a9#.lb3sscovr
RequireJS is another module loader, but he works not with CommonJs-style modules (require). He implements AMD-style modules.
You can check this article to understand how AMD is different from CommonJS:
https://auth0.com/blog/2016/03/15/javascript-module-systems-showdown/
I have created a class file in Node called chart.js that relies moment.js and a bunch of other external dependencies:
var moment = require('moment');
...
var Chart = {
doSomething: function() {
} ...
};
module.exports = Chart;
Now I want to use this chart.js module in my index.js file, so I am importing it as follows:
var chart = require('chart');
chart.doSomething();
But this gives me import errors:
ReferenceError: moment is not defined
Do I need to re-include all the require('moment') statements at the top of index.js too?
Surely I should be able to change the requirements for the chart file without having to amend the index file too?
No, you don't need to specify require('moment') in every file. Make sure moment JS library is installed correctly and that it is in your node_modules directory.
For Node modules installed with npm, you can require them with require('moduleName'), so require('moment') is fine. But for your own modules, you have to specify the relative path and the file name. So, assuming index.js and chart.js are in the same level, in your index.js you would do require('./chart.js').
Other notes: don't capitalize variable names unless you are using a class constructor function. In your code you are using simple object literal, not a constructor, so there's no need to capitalize the variable name, Chart.
I have a marionette project, and I have my main js file which looks like:
define(["marionette", "handlebars"], function(Marionette, Handlebars){
...
});
Now I want to make a structure of my big app. So I would like to separate the content of this function to different files. So how can I do something like:
define(["marionette", "handlebars"], function(Marionette, Handlebars){
include routes.js
include menu/model.js
include menu/views.js
...
});
I think require.js can help me, but I don't know how. Any ideas?
To require a file in the AMD structure (like the one you describe), you can give the path to the file in the array and get the exported value as argument of the callback like this.
define(
[
"marionette",
"handlebars",
"routes",
"menu/model",
"menu/views"
],
function(Marionette, Handlebars, Routes, Model, MenuView){
// ...
});
Since this is a very basic thing in AMD, you should really read the documentation of Require.JS (here) and explanation about AMD modules (like this one). You will find lot of information about the AMD structure, etc
If you prefer, browserify lets you to load front-end javascript modules using the require syntax. It looks like this.
// library includes
var Marionette = require('backbone.marionette');
var handlebars = require('handlebars');
// custom includes
var routes = require('./routes.js');
var models = require('./menu/models.js');
var views = require('./menu/views.js');
var App = new Marionette.Application();
It includes modules recursively and there is less code. It implements the Common.js format so it looks and feels the same as node.js.