I'm using Express.js (on Node.js) and I know that you can render a view with custom data via the "locals" parameter. (res.render("template", { locals: { foo: "bar" } });)
Is there any way to have "globals"? (ie. data that's accessible to every view)
I saw view options, but that isn't recursive, so it replaces the locals I set if I use any locals with my template.
This is my use case: I want to make it so that CSS/JS files can be added on a per-page basis, and that is part of my main layout. The problem is, if I don't explicitly set those arrays on every render, I get an undefined error, so in my template I always have to do the typeof css !== "undefined" dance. Additionally, I have other select box option lists that I don't want to have to explicitly add to each of my forms.
It's worth noting for those who may have come across this question since the release of Express 3, that the method 'dynamicHelpers' no longer exists.
Instead you can use the app.locals function, which acts as an object you can store values or functions in, and then makes them available to views. For example:-
// In your app.js etc.
app.locals.title = "My App";
app.locals({
version: 3,
somefunction: function() {
return "function result";
}
});
// Then in your templates (shown here using a jade template)
=title
=version
=somefunction()
// Will output
My App
3
function result
If you need access to the request object to pull information from, you can write a simple middle-ware function and use the app.settings variable.
For example, if you are using connect-flash to provide messages to your users, you might do something like this:
app.use(function(req, res, next) {
app.set('error', req.flash('error'));
next();
});
Which would give you access to the error message with =settings.error in your template.
These topics are covered here, albeit slightly briefly: http://expressjs.com/api.html#app.locals
Update: Express 4
app.locals is now a simple JavaScript Object, so every property has to be set one by one.
app.locals.version = 3;
app.locals.somefunction = function() {
return "function result";
}
res.locals provides the exact same functionality, except it should be used for request-specific data rather than application-wide data. A user object or settings is a common use case.
res.locals.user = req.isAuthenticated() ? req.user : null;
res.locals.userSettings = {
backgroundColor: 'fff'
}
There is a way to have "global" variables for views, using dynamic view helpers.
From the Express.js guide:
app.dynamicHelpers(obj)
Registers dynamic view helpers.
Dynamic view helpers are simply
functions which accept req, res, and
are evaluated against the Server
instance before a view is rendered.
The return value of this function
becomes the local variable it is
associated with.
app.dynamicHelpers({ session:
function(req, res){
return req.session; } });
All views would now have session
available so that session data can be
accessed via session.name etc:
You can find a real example on how to use them here: https://github.com/alessioalex/Nodetuts/tree/master/express_samples (node app.js to start the app)
A real-world example of using view options as the author mentioned:
var app = express.createServer();
app.configure(function() {
app.set('views', path.join(__dirname, '..', 'views'));
app.set('view engine', 'jade');
app.set('view options', {
assetVersion: 1
});
And then in my layout.jade (base template for the app in my case):
link(rel='stylesheet', href='/static/css/' + assetVersion + '/style.css')
script(src='/static/js/' + assetVersion + '/script.js')
With this little trick, I only have to update the assetVersion variable one place to make sure that my assets aren’t cached in Varnish or other places.
I wound up looking into the source code, and I've actually found that this is now possible in never versions of Express. (so far, only available through GitHub)
The simplest way to accomplish this is to create a variable that represents the default set of locals for your views. Then create a function that accepts an object, merges it with the locals, and returns the merged object.
I also pass ALL my locals inside a container object i.e. {locals:{g:{prop:val}}} so in my views I can refernce g.prop which will just return null when it isn't set, instead of throwing an undefined error.
function default_page_vars(custom_vars){
var vars = {
footer: true,
host: req.headers.host.split(':')[0],
config: this.config
};
if(custom_vars){
for(var k in custom_vars){
vars[k] = custom_vars[k];
}
}
return {
g:vars
};
}
//within your handler
response.render(view, {
locals: default_page_vars(other_locals)
});
This is a buried response, but I finally got it to work.
1) This is an example around the module connect-flash
2) Add a piece of middleware in server.js/app.js to add req to locals. This allows the template to call request.flash() whenever it needs. Without this, flash() gets consumed on each request/redirect defeating the purpose.
var app = module.exports = express()
, flash=require('connect-flash');
app.configure(function(){
...
app.use(express.session({ secret: "shhh" }));
// Start Router
app.use(flash());
app.use(function(req, res, next) {
res.locals.request = req;
next();
});
app.use(app.router);
});
3) Setup your route as normal (this is coffeescript, but nothing special)
app.get '/home', (req, res) ->
req.flash "info", "this"
res.render "#{__dirname}/views/index"
4) Call request.flash() when you want the messages. They are consumed on each call, so don't console.log them or they'll be gone :-)
!!!
html
head
title= config.appTitle
include partials/_styles
body
include partials/_scripts
#header
a(href="/logout") Logout CURRENTUSER
h2= config.appTitle
#messages
- var flash = request.flash()
each flashType in ['info','warn','error']
if flash[flashType]
p.flash(class=flashType)
= flash[flashType]
block content
h1 content here
Express 4
You can access local variables in templates rendered within the application.
So, if you want to use any locals in your template => assuming you have a template engine npm installed to your node/express application.
First, you need to set the express locals objects with your custom variables in your app.js file, you can use an object if multiple values are needed (our case in this post)
/**
* Set locals object
*/
app.locals.layoutData = {
site: {
title: 'MyWebSiteTitle',
},
metaTag: {
charset: 'UTF-8',
description: 'MyDescription',
keywords: 'keyword-1,keyword-2,...',
author: 'MyName',
viewport: 'width=device-width, initial-scale=1.0'
}
};
Then, to access the values in the template file layout.pug (in the case of PUG template engine for instance)
doctype html
html
head
//title
title #{locals.layoutData.site.title}
//Describe metadata
meta(charset=layoutData.metaTag.charset)
meta(name='description', content=locals.layoutData.metaTag.description)
meta(name='keywords', content=locals.layoutData.metaTag.keywords)
meta(name='author', content=locals.layoutData.metaTag.author)
meta(name='viewport', content=locals.layoutData.metaTag.viewport)
body
block content
br
hr
footer
p All rights reserved © 2018 | #{locals.layoutData.site.title}
Tested with
"dependencies": {
"express": "^4.16.3",
"pug": "^2.0.3"
}
Related
I want to pass some variable from the first middleware to another middleware, and I tried doing this, but there was "req.somevariable is a given as 'undefined'".
//app.js
..
app.get('/someurl/', middleware1, middleware2)
...
////middleware1
...
some conditions
...
res.somevariable = variable1;
next();
...
////middleware2
...
some conditions
...
variable = req.somevariable;
...
v4.x API docs
This is what the res.locals object is for. Setting variables directly on the request object is not supported or documented. res.locals is guaranteed to hold state over the life of a request.
Quote from the docs:
An object that contains response local variables scoped to the
request, and therefore available only to the view(s) rendered during
that request / response cycle (if any). Otherwise, this property is
identical to app.locals.
This property is useful for exposing request-level information such as
the request path name, authenticated user, user settings, and so on.
app.use(function(req, res, next) {
res.locals.user = req.user;
res.locals.authenticated = !req.user.anonymous;
next();
});
To retrieve the variable in the next middleware:
app.use(function(req, res, next) {
if (res.locals.authenticated) {
console.log(res.locals.user.id);
}
next();
});
Attach your variable to the res.locals object, not req.
Instead of
req.somevariable = variable1;
Have:
res.locals.somevariable = variable1;
As others have pointed out, res.locals is the recommended way of passing data through middleware.
I don't think that best practice will be passing a variable like req.YOUR_VAR. You might want to consider req.YOUR_APP_NAME.YOUR_VAR or req.mw_params.YOUR_VAR.
It will help you avoid overwriting other attributes.
The most common pattern for passing variables on to other middleware and endpoint functions is attaching values to the request object req.
In your case, that would mean having middlewares such as these:
app.use(function (req, res, next) {
req.someVariable = 123;
next();
});
app.use(function (req, res, next) {
console.log("The variable is", req.someVariable);
next();
});
There are many common use cases of this pattern, and it is the standard way of doing it in the express community. See, for example:
express.json, which ships with express, previously part of body-parser which follows the same pattern for all request parsing.
multer used for parsing multipart data
express-jwt
express-validator (see code)
express-session
express-useragent
express-pino-logger
express-bunyan-logger
It is worth noting that the currently most highly voted answer incorrectly recommends using res.locals for this purpose---which seems to stem from a misreading of the documentation. For that reason, I'll elaborate on why this is not the usual approach to the problem (although it isn't particularly harmful either).
The documentation
As supporting evidence for the res.locals approach being the appropriate one for the case, the now outdated documentation is cited:
An object that contains response local variables scoped to the request, and therefore available only to the view(s) rendered during that request / response cycle (if any). Otherwise, this property is identical to app.locals.
This property is useful for exposing request-level information such as the request path name, authenticated user, user settings, and so on.
Note the framing here: res.locals is for variables only available "to the view(s) rendered during that request" (emphasis added).
That is what res.locals relates to. res.render renders some template file with some given data as well as access to the locals. This was actually more clear in the v2 docs, and we've now updated the current Express documentation to be clearer:
Use this property to set variables accessible in templates rendered with res.render. The variables set on res.locals are available within a single request-response cycle, and will not be shared between requests.
In order to keep local variables for use in template rendering between requests, use app.locals instead.
This property is useful for exposing request-level information such as the request path name, authenticated user, user settings, and so on to templates rendered within the application.
(Emphasis added.)
The guide
Further evidence of extending req being the standard approach is found in the guide on Writing Middleware, which states:
Next, we’ll create a middleware function called “requestTime” and add a property called requestTime to the request object.
const requestTime = function (req, res, next) {
req.requestTime = Date.now()
next()
}
When this was mentioned in discussion in the answers on this here question, one user responded: "This was the way you'd do it before they added res.locals so might be old docs. res.locals is a namespace specifically for this."
This doesn't track with the history of the codebase, however: locals have been present since v2, which is significantly before e.g. express.json was included in the library, at which point it would have made sense to change the behvaior, if it was indeed correct to save values in res.locals.
Closing notes
Shoutout to #real_ate who wrote in the comments, but was overlooked.
That's because req and res are two different objects.
You need to look for the property on the same object you added it to.
The trick is pretty simple... The request cycle is still pretty much alive. You can just add a new variable that will create a temporary, calling
app.get('some/url/endpoint', middleware1, middleware2);
Since you can handle your request in the first middleware
(req, res, next) => {
var yourvalue = anyvalue
}
In middleware 1 you handle your logic and store your value like below:
req.anyvariable = yourvalue
In middleware 2 you can catch this value from middleware 1 doing the following:
(req, res, next) => {
var storedvalue = req.yourvalue
}
As mentioned above, res.locals is a good (recommended) way to do this. See here for a quick tutorial on how to do this in Express.
Hello I was wondering if there is a proper way to pass a variable or object to a layout view?
This is what I'm doing at the moment and it works
index: function(req, res){
res.view({ layout: 'mylayout', myvar: 'This is a view var' });
}
But on every action I have to define 'myvar' so I can use it at the layout level, so what I would like to know if there is some kind of controller or action for layouts so I can place there my logic?
Actually starting with Sails v0.10-rc5, you can use sails.config.views.locals hash to specify variables that should be provided by default to all views. So in config/views.js, something like:
{
locals: {
myvar : 'this is a view var'
}
}
would add the var to every view. This is mostly useful for variables that affect the view engine; see here for more details.
You can also use a policy in Sails v0.10.x to set vars across multiple views, by altering req.options.locals. So if you created a policy /api/policies/decorate.js with:
module.exports = function(req, res, next) {
// Default to an object if empty, or use existing locals that may have
// been set elsewhere
req.options.locals = req.options.locals || {};
// Set a new local var that will be available to any controller that
// implements the policy
req.options.locals.myVar = Math.random()
and then set up your /config/policies.js with something like:
module.exports = {
'*': 'decorate'
}
then any controller action that uses res.view will have that myVar variable available in the view.
It depends on the layout engine you have configured with sails (ejs is default). However passing a variable to a view is commonly done like this:
index: function(req, res){
res.view({ layout: 'mylayout', myModel: modelObj });
}
You have to inject the model in every view that uses it. If you want to register the model a globally you have to use a custom middleware like described here: How to create global variables accessible in all views using Express / Node.JS?
You can use config.views.locals, but i like to keep this out of the config folder and put it in the api/services directory. I call it api/services/page.js
then add some variable and other view helpers if you want.
module.exports = {
title:'test',
appName: 'My App Name',
getBlueprintPath: function (path) {
return sails.config.blueprints.prefix + path;
},
};
then in my view i have
<%= page.appName %>
the advantage of doing this way is your logic is kept inside api directory. You can still set locals for the view and not accidentally override some variable you had previously set in config.views.local.
I'm writing an app using expressjs. I have my views in, commonly, /views folder. They cover 90% of my customers' needs, but sometimes I have to override one or another of those view to add custom-tailored features. I really wonder I can build a folder structure like:
*{ ...other expressjs files and folders...}*
/views
view1.jade
view2.jade
view2.jade
/customerA
view2.jade
/customerB
view3.jade
What I'd like is to override the behaviour of expressjs' response.render() function to apply the following algorithm:
1. a customer requests a view
2. if /{customer_folder}/{view_name}.jade exists, than
render /{customer_folder}/{view_name}.jade
else
render /views/{view_name}.jade
Thus, for customerA, response.render('view1') will refer to /views/view1.jade while response.render('view2') will refer to /customerA/view2.jade
(those who use appcelerator's titanium may sound it familiar)
I'd like an elegant way to implement this behavior without the hassle of modify expressjs' core functionality, and thus possibly get treated at upgrading my framework. I guess it's a common problem but I can't find any article on the web.
I would create a custom View class:
var express = require('express');
var app = express();
var View = app.get('view');
var MyView = function(name, options) {
View.call(this, name, options);
};
MyView.prototype = Object.create(View.prototype);
MyView.prototype.lookup = function(path) {
// `path` contains the template name to look up, so here you can perform
// your customer-specific lookups and change `path` so that it points to
// the correct file for the customer...
...
// when done, just call the original lookup method.
return View.prototype.lookup.call(this, path);
};
app.set('view', MyView);
You can hook http.ServerResponse.render.
Here's some code from the top of my head, to be used as middleware:
var backup = res.render
res.render = function() {
//Do your thing with the arguments array, maybe use environment variables
backup.apply(res, arguments) //Function.prototype.apply calls a function in context of argument 1, with argument 2 being the argument array for the actual call
}
I want to pass some variable from the first middleware to another middleware, and I tried doing this, but there was "req.somevariable is a given as 'undefined'".
//app.js
..
app.get('/someurl/', middleware1, middleware2)
...
////middleware1
...
some conditions
...
res.somevariable = variable1;
next();
...
////middleware2
...
some conditions
...
variable = req.somevariable;
...
v4.x API docs
This is what the res.locals object is for. Setting variables directly on the request object is not supported or documented. res.locals is guaranteed to hold state over the life of a request.
Quote from the docs:
An object that contains response local variables scoped to the
request, and therefore available only to the view(s) rendered during
that request / response cycle (if any). Otherwise, this property is
identical to app.locals.
This property is useful for exposing request-level information such as
the request path name, authenticated user, user settings, and so on.
app.use(function(req, res, next) {
res.locals.user = req.user;
res.locals.authenticated = !req.user.anonymous;
next();
});
To retrieve the variable in the next middleware:
app.use(function(req, res, next) {
if (res.locals.authenticated) {
console.log(res.locals.user.id);
}
next();
});
Attach your variable to the res.locals object, not req.
Instead of
req.somevariable = variable1;
Have:
res.locals.somevariable = variable1;
As others have pointed out, res.locals is the recommended way of passing data through middleware.
I don't think that best practice will be passing a variable like req.YOUR_VAR. You might want to consider req.YOUR_APP_NAME.YOUR_VAR or req.mw_params.YOUR_VAR.
It will help you avoid overwriting other attributes.
The most common pattern for passing variables on to other middleware and endpoint functions is attaching values to the request object req.
In your case, that would mean having middlewares such as these:
app.use(function (req, res, next) {
req.someVariable = 123;
next();
});
app.use(function (req, res, next) {
console.log("The variable is", req.someVariable);
next();
});
There are many common use cases of this pattern, and it is the standard way of doing it in the express community. See, for example:
express.json, which ships with express, previously part of body-parser which follows the same pattern for all request parsing.
multer used for parsing multipart data
express-jwt
express-validator (see code)
express-session
express-useragent
express-pino-logger
express-bunyan-logger
It is worth noting that the currently most highly voted answer incorrectly recommends using res.locals for this purpose---which seems to stem from a misreading of the documentation. For that reason, I'll elaborate on why this is not the usual approach to the problem (although it isn't particularly harmful either).
The documentation
As supporting evidence for the res.locals approach being the appropriate one for the case, the now outdated documentation is cited:
An object that contains response local variables scoped to the request, and therefore available only to the view(s) rendered during that request / response cycle (if any). Otherwise, this property is identical to app.locals.
This property is useful for exposing request-level information such as the request path name, authenticated user, user settings, and so on.
Note the framing here: res.locals is for variables only available "to the view(s) rendered during that request" (emphasis added).
That is what res.locals relates to. res.render renders some template file with some given data as well as access to the locals. This was actually more clear in the v2 docs, and we've now updated the current Express documentation to be clearer:
Use this property to set variables accessible in templates rendered with res.render. The variables set on res.locals are available within a single request-response cycle, and will not be shared between requests.
In order to keep local variables for use in template rendering between requests, use app.locals instead.
This property is useful for exposing request-level information such as the request path name, authenticated user, user settings, and so on to templates rendered within the application.
(Emphasis added.)
The guide
Further evidence of extending req being the standard approach is found in the guide on Writing Middleware, which states:
Next, we’ll create a middleware function called “requestTime” and add a property called requestTime to the request object.
const requestTime = function (req, res, next) {
req.requestTime = Date.now()
next()
}
When this was mentioned in discussion in the answers on this here question, one user responded: "This was the way you'd do it before they added res.locals so might be old docs. res.locals is a namespace specifically for this."
This doesn't track with the history of the codebase, however: locals have been present since v2, which is significantly before e.g. express.json was included in the library, at which point it would have made sense to change the behvaior, if it was indeed correct to save values in res.locals.
Closing notes
Shoutout to #real_ate who wrote in the comments, but was overlooked.
That's because req and res are two different objects.
You need to look for the property on the same object you added it to.
The trick is pretty simple... The request cycle is still pretty much alive. You can just add a new variable that will create a temporary, calling
app.get('some/url/endpoint', middleware1, middleware2);
Since you can handle your request in the first middleware
(req, res, next) => {
var yourvalue = anyvalue
}
In middleware 1 you handle your logic and store your value like below:
req.anyvariable = yourvalue
In middleware 2 you can catch this value from middleware 1 doing the following:
(req, res, next) => {
var storedvalue = req.yourvalue
}
As mentioned above, res.locals is a good (recommended) way to do this. See here for a quick tutorial on how to do this in Express.
I'm in the process of learning Express - and thinking of the best place to save config style data. Options available are either in app.locals or app.set (settings)... so:
app.locals({ config: {
name: 'My App',
domain: 'myapp.com',
viewPath: __dirname+'/views',
viewEngine: 'jade'
port: 3000
} });
app.set('view engine', app.locals.config.viewEngine || 'jade');
This would also allow me to use the following in my views:
<title>#{config.name}</title> // <title>My App</title>
Or the alternative is to use app.set like so:
app.set('name', 'My App');
app.set('domain', 'myapp.com');
... and then use this in the view:
<title>#{settings.name}</title>
I know both methods work, but I'm struggling to determine which is the better method to use. At the moment I'm leaning towards using app.locals, with the extra 'app' namespace as I believe there would be less chance of conflicts with future updates and other modules if using app.set.
Wow, all of the answers are wrong, so let me give it a try. Despite what others say assinging to the app.local argument is different from using app.set(). Watch,
app.js
app.locals.foo = 'bar';
app.set('baz', 'quz');
index.jade
block content
dl
dt app.locals.foo = 'bar';
dd \#{settings.foo} = #{settings.foo}
dd \#{foo} = #{foo}
dt app.set('baz', 'quz')
dd \#{settings.baz} = #{settings.baz}
dd \#{baz} = #{baz}
If you ran this code, what you would see is,
app.locals.foo = 'bar';
#{settings.foo} =
#{foo} = bar
app.set('baz', 'quz')
#{settings.baz} = quz
#{baz} =
The reason for this is setting app.locals sets attributes of the object that the view uses as its environment; what the view will read from without qualification. Conversely, app.set sets attributes on app.locals.settings. You can verify this if you clobber app.locals.settings in the above with app.locals.settings = {}, which will make #{settings.baz} undefined.
So which do you use? If it's not an app setting based on the response (res.set) or global configuration (app.set), use the direct write to app.locals.
All properties of app.locals are available in templates. Using app.set assigns properties to app.locals.settings, which is used for global application settings and is inherited by mounted applications. For example:
var app1 = express(),
app2 = express();
app1.set('inheritable', 'foo');
app1.locals.notInheritable = 'bar';
app1.use('/mount', app2);
app2.get('inheritable') === 'foo'; // true
app2.locals.notInheritable === 'bar'; // false
So it's really a question of preference and whether or not you're mounting applications.
The express api reference says :
By default Express exposes only a single app-level local variable,
settings.
and either way is ok:
app.locals.title = 'My App';
app.set('title', 'My App');
// use settings.title in a view
Lots of people indeed use locals instead of app.set so my advice is to use that.