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

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);

Related

What's the purpose of requiring ejs?

In many code examples for ejs, I see code like this:
// ...
const ejs = require('ejs');
// ...
app.set('view engine', 'ejs');
app.get('/', function(req, res) {
res.render('index');
});
// ...
But it seems like the constant ejs is never used. Then what's the purpose of requiring it?
It doesn't seem to make any difference when I choose to require or not requiring ejs.
We can set ejs template by using: app.set('view engine', 'ejs');
And here we don't require any ejs import.
But situation can come where you to have render some ejs file and then pass on data and do some stuff with this template, then in those scenario you have to require ejs and then use its methods for it. For example:
// Template example for ejs:
const ejs = require('ejs');
ejs.renderFile(
path.join(__dirname, '../../views/pages/your_ejs_file.ejs'),
your_data,
);
That's just one use case.
https://www.npmjs.com/package/ejs
You are setting the EJS Template Engine by:
app.set('view engine', 'ejs');
But you can also use the ejs for performing caching operation like:
const ejs = require('ejs'),
const LRU = require('lru-cache');
ejs.cache = LRU(100); // LRU cache with 100-item limit
EJS is used to cache the JS function used to render templatees. And if want to clear the EJS caache you can simply call the ejs.clearCache. This is just an example but you can use ejs in many ways. More about Ejs

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));

Subfolder views expressjs 3x /nodejs

How can I use a subfolder for my 'views'?
In the main 'app.js' I've set the root folder for the views like so:
app.configure(function(){
app.set('view engine', 'jade');
app.set('views', __dirname + '/apps' );
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.static(__dirname + '/apps' ));
app.use(app.router);
});
And my controller loos like this:
module.exports = function(req, res, base) {
res.render( 'hello/views/');
};
The folder looks like this:
./apps/hello/views/index.js
But still it can't find it. "ERROR: Failed to lookup view "hello/views"
Thanks!
Actually, I made a vastly better solution.
In the app configuration I just set the default view like so:"
app.set('view engine', 'jade');
app.set('views', __dirname + '/views');
Later on I inlcude a boot.js (based on the mvc example of expressjs ) in which I include the following:
// allow specifying the view engine
if (obj.engine) app.set('view engine', obj.engine);
app.set('views', __dirname + '/../apps/' + name + '/views');
This will override the view folder to the one specified in the folder 'apps/xxx/views' and will even allow you to specify a new viewtype in the root of that folder like so:
// filename = /apps/hello/index.js
exports.engine = 'jade';
// Render the indexpage
exports.index = function(req, res, next){
res.render('hello');
};
I am surprised that your self answer works, since you should be telling express to render a particular view, and hello/views is a folder. However I'm not familiar with the code pattern you're using for your controller (the base argument is what's throwing me off, since Express middleware uses function(req,res,next).)
My routes look like this:
app.set('views', __dirname + '/apps' );
...
app.get('/hello', function(req,res){
res.render('hello/views/index')
});
In this case ./apps/hello/views/index.jade is a jade file, not a javascript file. If your templates are javascript, perhaps you should set view engine to something other than jade.
You can set views as array of paths
app.set('views', [path.join(__dirname, 'views/users/'), path.join(__dirname, 'views')])
You can check the resulting paths using app.get('views'); which should return an array.
[/server/views/users/, /server/views]
Express will search through the array for available paths. You can then render with just the file name like this
res.render( 'index');
res.render( 'profile');
I fixed this problem by setting the basis view to the root of the directory like so:
app.set('views', __dirname);
And the added the folders from the root on in the controller like so:
res.render( 'apps/hello/views/');

Having a Node.JS Express template engine with multiple templates in one file

I wrote a custom template engine to let me use my old templates in a rewrite of my current project. Everything is working except that Express.js always sends a
Error: Failed to lookup view (template name)
I have multiple templates in one file and I don't need one file for each template. But how can I tell that to Express?
I can't get my head around this.
Any help is appreciated
EDIT: Some Code:
My Express Setup: (app.js)
var express = require('express');
var app = express();
var gclosure = require('./templateEngine');
var fs = require('fs');
/**
* Setup Express
*/
app.configure(function(){
app.engine('test', gclosure.__express);
app.set('views', __dirname + '/public/templates');
app.set('view engine', 'test');
app.use(express.favicon());
app.use(express.bodyParser());
app.use(express.cookieParser());
app.use(express.methodOverride());
app.use(require('less-middleware')({
src: __dirname + '/public'
}));
app.use(express.static(__dirname + '/public'));
});
My Custom Template Engine (templateEngine.js)
exports.renderFile = function(path, options, fn){
if ('function' == typeof options) {
fn = options, options = {};
}
try {
fm(null, "This is a test output");
} catch (err) {
fn(err);
}
};
My test route in express:
app.get('/', function(req, res, next) {
res.render('simple',{
title: 'Sources',
data: 'lala',
options: 'miep'
});
});
The Clue is: When I create a file named simple.test in the template folder everything is working and I can see the test output string. When I change the name to something that doesn't exist the out put is
Error: Failed to lookup view (template name)
The problem:
Express uses the template engine to create a view object which renders the view. Since the view itself is created by looking up the file into the filesystem the template engine has no way of telling express that it handles the files on its own.
The Solution:
Instead of using a template engine approach I wrote a custom middle-ware that intercepts calls to my template engine depending on the name of the template function. Basically I overwrite the response.render() function in express.
Update: GitHub Repo

Resources