Im using pm2 to handle my nodejs micro services and express-handlebars to handle the views:
var hbs = exphbs.create({
defaultLayout: 'main',
helpers: {
ifeq: function(a, b, options) {
if (a === b) {
return options.fn(this);
}
return options.inverse(this);
},
toJSON : function(object) {
return JSON.stringify(object);
}
}
});
app.engine('handlebars', hbs.engine);
app.set('view engine', 'handlebars');
Lunching the app directly (node app.js) works great. But if I lunch it using PM2 (pm2 start app.js) i get:
Error: Failed to lookup view "home" in views directory "/root/views"
When lunching pm2 the current working directory change to /root/ and since my app in not there I got an error from handlebars trying to open the views directory (which is in the app directory).
Is there a way to fix this by telling pm2 the current working directory or by telling the express-handlebars library the complete directory instead of using a relative one?
I am using Koa and SWIG for templates - but I needed to include the path to the views in the app setup:
app.use(koaRender('./server/server-side-views', {
map: { html: 'swig' },
cache: false
}));
I suspect it is more of the same for you. I think in express your code should be something along the line of:
app.engine('handlebars', hbs.engine);
app.set('view engine', 'handlebars');
app.set('views', __dirname + '/yourViewDirectory');
Related
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);
I have a mean-stack application. By going to https://localhost:3000/#/home, it reads views/index.ejs. Here is the setting in app.js:
var app = express();
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use(express.static(path.join(__dirname, 'public')));
app.all('/*', function(req, res, next) {
res.sendFile('index.ejs', { root: __dirname });
});
Actually, I don't use the feature of ejs in index.ejs. So now I want to use just a index.html rather than index.ejs.
I put the content of index.ejs in public/htmls/index.html and views/index.html. And here is the current setting in app.js:
var app = express();
// app.set('views', path.join(__dirname, 'views'));
// app.set('view engine', 'ejs');
app.use(express.static(path.join(__dirname, 'public')));
app.all('/*', function(req, res, next) {
res.sendFile('index.html', { root: __dirname });
// res.sendFile('index.html'); // does not work either
});
However, running https://localhost:3000/#/home returns
Error: No default engine was specified and no extension was provided.
Does anyone know how to fix it?
Edit 1: by following the answer of user818510, I tried res.sendFile('index.html', { root: path.join(__dirname, 'views') }); in app.js, it still can NOT find index.html.
Whereas, in routes/index.js, the following can find index.html, but it gives a warning express deprecated res.sendfile: Use res.sendFile instead routes/index.js:460:9.
var express = require('express');
var router = express.Router();
var path = require('path');
... ...
router.get('*', function(req, res) {
res.sendfile('./views/index.html'); // works, but a deprecation warning
// res.sendFile('index.html', { root: path.join(__dirname, 'views') }); does not work
});
It is really confusing...
If it's a single page mean application, then you only need to start express with static and put index.html in static/ dir :
Project layout
static/
index.html
server.js
server.js
'use strict';
var express = require('express');
var app = express();
app.use(express.static('public'));
var server = app.listen(8888, function () {
console.log("Server started. Listening on port %s", server.address().port);
});
Now you can call http://localhost:8888/#home
It looks like a problem with the path. Your index.html is located at public/htmls/index.html and views/index.html. Your root option in res.sendFile should be __dirname+/public/htmls/ or __dirname+/views
In your code, you are using the path:
res.sendFile('index.html', { root: __dirname });
Your app.js would be in the project root where you have public directory alongside at the same level. Based on your rootoption in res.sendFile, you would have to place index.html at the same level as your app.js.
You should change the root path in res.sendFile. Use:
res.sendFile('index.html', { root: path.join(__dirname, 'public', 'htmls') });
OR
res.sendFile('index.html', { root: path.join(__dirname, 'views') });
The above root is based on the path that you've mentioned in your question.
Here's the link to the docs:
http://expressjs.com/en/api.html#res.sendFile
Your no default engine error is probably because you have commented the line where you set the view engine to ejs but still have existing ejs views. Uncommenting that line with the root path change should solve your issue.
Do not serve static content from an application server.
Use a web server for that, and in production, a content delivery network like Akamai.
A content delivery network will charge you per bandwidth (e.g: 10 cents per Terabyte). Serving the equivalent of 10 cents in Akamai can cost you thousands of dollars using cloud instances.
In addition to that, your servers will have unnecessary load.
If you absolutely have to serve static content from your application servers, then put a reverse proxy cache like nginx, varnish or squid in front of your server. But that will still be very cost inefficient. This is documented in the express website.
This is common practice in every Internet company.
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.
This is the structure of my project.
app
-controllers
index.server.controller.js
-models
-routes
index.server.routes.js
-views
index.ejs
config
-express.js
I set the view directory in my express.js file:
app.set('views', '../app/views');
app.set('view engine', 'ejs');
And this is in my controller file:
exports.render = function(req,res) {
res.render('index' , {
title: 'welcome to this page'
})
};
Whenever i open localhost i get
Error: Failed to lookup view "index" in views directory "../app/views/"
You're giving it a wrong path. Try this
app.set('views', __dirname + '/../app/views');
I'm attempting to write my own handlebars helpers and am getting no where. I'm using the npm hbs package (Handlebars.registerHelper didn't work) and registering it in app.js like so:
app.set('view engine', 'hbs');
var hbs = require('hbs');
hbs.registerHelper("log", function(something) {
return console.log(something);
});
hbs.registerHelper('test', function() {
return console.log('test')
});
Yet {{log 'test'}} or {{test}} any where inside my template does absolutely nothing. There's no js errors being produced in the browser or terminal consoles. I know handle bars is working correctly since I have other hb variables displaying correctly. I'm at my wits end here trying to do something very simple otherwise I wouldn't embarrasses my self by asking such a simple question.
Thank you for your time.
Thank you for your input mscdex but your response didn't work. I'll clarify how I got it working here for others new to handlebars. I was using the npm hbs package because I found good helper examples. After looking into it deeper, my previous handlebars package "express-handlebars" had some examples on the npm page that actually worked. I think my main confusion was a result of not knowing where the first variable in the declaration was being defined.
Below is the code in my app.js file that declares which handlebars package to use.
var exphbs = require('express-handlebars');
app.engine('.hbs', exphbs({defaultLayout: 'main', extname: '.hbs'}));
app.set('view engine', 'hbs');
Then in the route for a particular page the helper is defined the same way the title or an array of data would be:
res.render('editSite', {
title: 'Update Existing Site\'s Data', siteVars: siteVars, helpers: {
foo: function () { return console.log('test'); }
}
});
You need to actually return a string from your helpers. console.log() returns undefined. Try this instead:
hbs.registerHelper("log", function(something) {
console.log(something);
return ''+something;
});
hbs.registerHelper('test', function() {
console.log('test');
return 'test';
});
in my view, the simpliest way to do what you want, if this is about global helpers (may not be the best solution, but...), is to create your helpers file :
module.exports = {
customif: (options)=>{
return (options.hash.expected === options.hash.val) ? options.fn(this) : options.inverse(this);
}
}
Then, require it :
const handlebarsHelpers = require('./helpers/handlebars');
And finally, when you link express to handlebars, you can insert helpers in the object :
app.engine('hbs', handlebars({
extname: 'hbs',
defaultLayout: 'layout',
layoutsDir: __dirname + '/views/layouts/',
helpers: handlebarsHelpers
}));
And you can use it inside your layout blabla.hbs:
{{#customif expected='foo' val=field}}
bar
{{/customif}}
Works for me...