Node.js - Migrating from Swig to Nunjucks in Express - node.js

I have a Node.js app. I've been building this app for a while and it currently uses Swig as the view engine. I'm setting it as the view engine using the following code:
// Use swig.
const swig = require('swig');
app.engine('html', swig.renderFile);
if (app.get('env') === 'development') {
swig.setDefaults({ cache: false });
}
app.set('views', path.join(__dirname, '../views'));
app.set('view engine', 'html');
This has been working well. However, I have some downtime so I thought now would be a good time to migrate to Nunjucks. So, I replaced the above with:
// Use nunjucks.
const nunjucks = require('nunjucks');
app.engine('html', nunjucks.renderFile);
if (app.get('env') === 'development') {
nunjucks.setDefaults({ cache: false });
}
app.set('views', path.join(__dirname, '../views'));
app.set('view engine', 'html');
When I start up my site, I now get an error. The error is:
throw new Error('callback function required');
^
Error: callback function required
at EventEmitter.engine (C:\MyProject\node_modules\express\lib\application.js:294:11)
at EventEmitter.module.exports (C:\MyProject\src\index.js:16:9)
at EventEmitter.configure
...
What am I doing wrong? What callback is being sought after? I know I'm going to have some syntactical errors once I start using the Nunjucks engine. However, I'm just trying to figure out how to get the Nunjucks engine loaded.

Templating engines usually have their own method of configuration. For Nunjucks, you should use this:
const nunjucks = require('nunjucks');
nunjucks.configure('views', {
express : app,
noCache : app.get('env') === 'development',
...
});
Documentation here.

Related

Multiple View paths on Node.js + Express + <%= EJS %>

I am using express 4.17.1 combination for my web app.
I am wondering how can i render multiple files depend on the route and multiple folders
Let's say that i have :
When I want to go to the admin path this problem appears :
Passing an array of directory will not work if you're using express version 3 or above. Try the below
app.set('view engine', 'ejs');
var renderer = express.response.render;
express.render = () => {
app.set('views', './regular/viewPath/views');
try {
return renderer.apply(this, arguments);
}
catch (e) {...}
app.set('views', './admin/viewPath/views');
return renderer.apply(this, arguments);
};
const adminRoute = require('./routes/adminFile');
app.use('/admin', adminRoute);
Finally, set the name inside your admin file
router.set('name', 'adminFile');
Other easy alternative would be to put your views in different folders
Example:
views/index.ejs
views/admin/adminFile.ejs
app.set('views', path.join(__dirname, 'views'));
Then on render, you do like
res.render('views/admin/adminFile', configs);

Handlebars registerHelper serverside with Expressjs

I am using expressjs with handlebars as templating engine with following code in Webstorm IDE with express generator.There is no visible handlebars require in the code (I guess express generator has it someplace else which is not visible)
var app = express();
.
.
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'hbs');
How do i use registerHelper on serverside in this situation ?
My other renderings and partials are working.So handlebars is doing its work.Its just that registerHelper seems to be cause of worry.
I have given a thumbs up to #Mukesh Sharma as he very nearly had the syntax that worked for me and really led me to it.
He is doing a bit more to make it work for the front end I believe. All I needed was the following
// index.js
//in the declarations
const exphbs = require('express-handlebars');
//when configuring the app view engine
app.engine('.hbs', exphbs({
extname: '.hbs',
helpers: require('./config/handlebars-helpers') //only need this
}));
app.set('view engine', '.hbs');
Then I have a simple file I include from where I have helper stuff .
// config/handlebars-helpers.js
module.exports = {
ifeq: function(a, b, options){
if (a === b) {
return options.fn(this);
}
return options.inverse(this);
},
bar: function(){
return "BAR!";
}
}
No need to pass or import handlebars-express - including the simple object of helper functions as part of the exhbs options hash under helpers lets express hbs do all of the registering by it's own approach.
(Bonus ifeq is a tiny helper that compares two arguments and will show the block if true .. useful for something like:
class = "{{#ifeq thisUrl '/about'}}active{{/ifeq}}" .. to set a nav pill class as 'active' )
found it here https://gist.github.com/pheuter/3515945
I think express-generator just sets view engine to hbs only. To configure the hbs engine, you have to use express-handlebars.
e.g.
var app = express(),
exphbs = require("express-handlebars");
app.engine("hbs", exphbs({
defaultLayout: "main",
extname: ".hbs",
helpers: require("./public/js/helpers.js").helpers, // same file that gets used on our client
partialsDir: "views/partials/", // same as default, I just like to be explicit
layoutsDir: "views/layouts/" // same as default, I just like to be explicit
}));
app.set("view engine", "hbs");
And, helpers.js
var register = function(Handlebars) {
var helpers = {
// put all of your helpers inside this object
foo: function(){
return "FOO";
},
bar: function(){
return "BAR";
}
};
if (Handlebars && typeof Handlebars.registerHelper === "function") {
// register helpers
for (var prop in helpers) {
Handlebars.registerHelper(prop, helpers[prop]);
}
} else {
// just return helpers object if we can't register helpers here
return helpers;
}
};
module.exports.register = register;
module.exports.helpers = register(null);
Source: http://www.codyrushing.com/using-handlebars-helpers-on-both-client-and-server/
You can register both your own customer helpers and the ones from 'handlebars-helpers' like this:
const hbshelpers = require('handlebars-helpers');
const multihelpers = hbshelpers(['object', 'string']);
const helpersDM = {
hlp: echo => `Echo: ${echo}.`,
STATIC: `/static`,
};
const hbs = exphbs.create({
layoutsDir: join(__dirname, 'views', 'layouts'),
partialsDir: join(__dirname, 'views', 'partials'),
extname: '.hbs',
defaultLayout: 'base',
helpers: {...multihelpers, ...helpersDM},
});
app.engine('.hbs', hbs.engine);
app.setViewEngine('.hbs');
I was able to add partial and register a helper using hbs.
Seems to be it is the same view engine used within express for templating with handlebars.
According to hbs docs, add the following to the entry point of the application: (index.js or app.js) and before setting the view engine in express.
index.js
const hbs = require('hbs');
const express = require('express');
hbs.registerHelper('number_format', function (option) {
// option :
// will contain the arguments passed
// to the helper from the template
// computation can be done here
return `USD ${option}`;
});
hbs.registerPartial('partial', function (options, ...rest) {
// options :
// will contain the arguments passed
// to the partial from the template
// as a object (key value pair)
// ex:
console.log(option.argOne) // 1
console.log(option.argTwo) // 2
// ----------
// Have a look at the other arguments like context data
console.log(...rest)
// computation can be done here by using argument values
return `partial value: ARG1:: ${option.argOne} & ARG2:: ${option.argTwo}`;
});
const app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'hbs');
Partial in a hbs template file
<h1>
{{>partial argOne=1 argTwo=2 }}
</h2>
above will generate the following,
<h1>partial value: ARG1:: 1 & ARG2:: 2<h1>
Helper in a hbs template file
<h1>{{number_format 100 }}</h1>
above will generate the following,
<h1>USD 100</h1>
learn more about handlebars partials using their docs on partials

Disable view cache for pug (jade) templates in express

I use "express": "^4.14.0", "pug": "^2.0.0-beta6"
app.set('view engine', 'jade');
...
app.get('/', function (req, res) {
res.render('index.pug', {...});
}
When I use Express render function, it renders a template just once. If I change pug-template I'll get an old page version based on an already compiled template. For dev purposes, I need express recompiling .pug template for every render call. How can I achieve this?
I tried something like:
app.disable('view cache'); OR
app.set('view cache', false); OR
app.set('view cache', 'disabled');
But none of those were helpful.
Disappointing, but working:
const pug = require('pug');
const path = require('path');
res.send(pug.renderFile(path.join(__dirname,'template.pug'), templateObj));

How to render Swig templates with Express 4?

I am not able to use Swig templates with Express for Node.
I get the following error :
Error: Failed to lookup view "index" in views directory
The Swig templates are not being compiled into .html files
My code is below :
var express = require('express');
var cons = require('consolidate');
var swig = require('swig');
var app = express();
//Set template engine
app.engine('html', cons.swig);
app.set('view engine', 'html');
app.set('views', __dirname + '/views')
// Disable Express's and Swig Cache
app.set('view cache', false);
swig.setDefaults({ cache: false });
app.get('/', function(req, res){
res.render('index', {'title': 'Design'});
});
app.listen(3000);
I just had the same issue. In order to use swig templates with the swig extension working you can follow the swig Docs and then replace the two following lines with
app.engine('html', cons.swig);
app.set('view engine', 'html');
with
app.engine('swig',swig.renderFile);
app.set('view engine', 'html');
Should look something like this.
var express = require('express');
var swig = require('swig');
var app = express();
// view engine setup
// This is where all the magic happens!
app.engine('swig', swig.renderFile);
app.set('view engine', 'swig');
app.set('views', path.join(__dirname, 'views'));
app.set('view cache', false);
swig.setDefaults({ cache: false });
Expressjs 4.x docs app.engine
The app.engine takes a extension and a callback. So to all files found with the swig extension have a callback of swig.renderFile. Each file with generated to HTML.
Expressjs 4.x docs app.set
Setting the view engineto swig is setting the default engine extension to use when omitted. Were also saying that all the views can be found in the /blaha/blaha/blah/views directory.
NOTE: All files in the views directory must end int .swig.
So just a friendly reminder make sure when you "include" or reference another template you must put the filename and extension it the path. ex: layout.swig. I hope this gets your project working.
As explained here:
http://mherman.org/blog/2015/08/23/primer-on-swig-templating/#.VhWxVvlVhBc
Simply use .html files instead of .swig files. I don't know where you got the idea of using .swig files. Maybe an older version? I dunno. The current Swig's documentation Basic Usage shows using a .html file as well:
http://paularmstrong.github.io/swig/docs/
If you rename your index.swig to index.html I would expect it to work fine.
Yes, i think it's more semantic to store your templates as .swig files, if you using SWIG as template engine. So, all you need is set 'swig' as engine:
// view engine setup
app.engine('swig', swig.renderFile);
app.set('view engine', 'swig');
app.set('views', path.join(__dirname, 'views'));
Then you can store your views like ./views/layout.swig
for latest version of swig we need to include swig package which needed to installed through package manager or package.json file
var express = require('express');
var swig = require('swig');
var app = express();
// Here we need to set up and engine as per latest version of swig
//for latest version of swig we need to include swig package which //needed to installed through package manager or package.json file
app.engine('swig', swig.renderFile);
app.set('view engine', 'swig');
app.set('views', path.join(__dirname, 'views'));
app.set('view cache', false);
swig.setDefaults({ cache: false });
//Here we are setting cache defaults to false.
`

Access i18next Translation in Function not in App.js

Im using ExpressJS and i18next.
in app.js
var express = require('express')
, i18n = require('i18next')
, user = require('./routes/user')
...
//internationalization
i18n.init({
lng: 'en-US',
saveMissing: true,
debug: true
});
...
app.use(i18n.handle);
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
...
i18n.registerAppHelper(app);
...
app.post('/users/save', user.save);
I can access the translation in jade:
t('app.title')
How can I access the translation in routes.user.js
exports.save = function(req, res){
//t('app.title')
}
t is available in your route handlers as res.locals.t. This should work in Express 3 and 2 as well.
i18next-express-middleware -> the translation function could be accessed through req.i18n.t or just req.t. but obviously it's under res.locals.t too - to be accessible in templates.

Resources