Express.js View “globals”

Question!

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.



Answers

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


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


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)



This video can help you solving your question :)
By: admin