Moshe Simantov
Moshe Simantov

Reputation: 4503

Multiple view paths in Express (node.js)

I'm writing a CMS on Node.js with Express Framework. On my CMS I have several modules for users, pages, etc.

I want that each module will have his files on separate folder, including the view files. Anyone know how can I achieve that?

I'm using swig as my template engine but I can replace it to something else if it will helps.

Upvotes: 50

Views: 31457

Answers (6)

Manish Gupta
Manish Gupta

Reputation: 1

For all the people who are still looking for the answer. Mention the paths in an array. ex:

app.set('views', [
path.join(path.resolve(), 'src', 'features', 'user', 'views'),
path.join(path.resolve(), 'src', 'features', 'home', 'view')]);

Upvotes: 0

Grady Woodruff
Grady Woodruff

Reputation: 143

Install glob npm install glob

If you have a views directory that looks something like:

views
├── 404.ejs
├── home.ejs
├── includes
│   ├── header.ejs
│   └── footer.ejs
├── post
│   ├── create.ejs
│   └── edit.ejs
└── profile.ejs

You can use this glob function to return an array of subdirectories in the views directory (add the path.substring to remove the trailing /)

let viewPaths = glob.sync('views/**/').map(path => {
    return path.substring(0, path.length - 1)
})


console.log(viewPaths)
>> ['views', 'views/post', 'views/includes']

So now you can set

app.set('views', viewPaths)

and now you can use

res.render('404')
res.render('home')
res.render('post/edit')
res.render('post/create')

Upvotes: 1

BFil
BFil

Reputation: 13096

Last Update

The multiple view folders feature is supported by the framework since Express 4.10

Just pass an array of locations to the views property, like so.

app.set('views', [__dirname + '/viewsFolder1', __dirname + '/viewsFolder2']);

Express 2.0

As far as I know express doesn't support multiple view paths or namespaces at the moment (like the static middleware do)

But you can modify the lookup logic yourself so that it works the way you want, for example:

function enableMultipleViewFolders(express) {
    // proxy function to the default view lookup
    var lookupProxy = express.view.lookup;

    express.view.lookup = function (view, options) {
        if (options.root instanceof Array) {
            // clones the options object
            var opts = {};
            for (var key in options) opts[key] = options[key];

            // loops through the paths and tries to match the view
            var matchedView = null,
                roots = opts.root;
            for (var i=0; i<roots.length; i++) {
                opts.root = roots[i];
                matchedView = lookupProxy.call(this, view, opts);
                if (matchedView.exists) break;
            }
            return matchedView;
        }

        return lookupProxy.call(express.view, view, options)
    };
}

You will enable the new logic by calling the function above and passing express as a parameter, and then you will be able to specify an array of views to the configuration:

var express = require('express');
enableMultipleViewFolders(express);
app.set('views', [__dirname + '/viewsFolder1', __dirname + '/viewsFolder2']);

Or, if you prefer, you can patch the framework directly (updating the view.js file inside it)

This should work in Express 2.x, not sure if it will with the new version (3.x)

UPDATE

Unluckily the above solution won't work in Express 3.x since express.view would be undefined

Another possible solution will be to proxy the response.render function and set the views folder config until it gets a match:

var renderProxy = express.response.render;
express.render = function(){
    app.set('views', 'path/to/custom/views');
    try {
        return renderProxy.apply(this, arguments);
    }
    catch (e) {}
    app.set('views', 'path/to/default/views');       
    return renderProxy.apply(this, arguments);
};

I've not tested it, it feels very hacky to me anyway, unluckily this feature has been pushed back again: https://github.com/visionmedia/express/pull/1186

UPDATE 2

This feature has been added in Express 4.10, since the following pull request has been merged: https://github.com/strongloop/express/pull/2320

Upvotes: 57

priyabagus
priyabagus

Reputation: 2878

You can however, put all the view files inside the 'view' folder, but separate each module's view into it's own folders inside the 'view' folder. So, the structure is something like this :

views  
--moduleA    
--moduleB  
----submoduleB1  
----submoduleB2  
--moduleC  

Set the view files like usual :

app.set('views', './views');

And when render for each module, include the module's name :

res.render('moduleA/index', ...);

or even submodule's name :

res.render('moduleB/submoduleB1/index', ...);

This solution is also works in express before version 4.x,

Upvotes: 6

BananaAcid
BananaAcid

Reputation: 3481

In addition to @user85461 answer, the require view part did not work for me. What i did: removed the path stuff and moved it all to a module i could require, patch.ViewEnableMultiFolders.js (Works with current express):

function ViewEnableMultiFolders(app) {
    // Monkey-patch express to accept multiple paths for looking up views.
    // this path may change depending on your setup.
    var lookup_proxy = app.get('view').prototype.lookup;

    app.get('view').prototype.lookup = function(viewName) {
        var context, match;
        if (this.root instanceof Array) {
            for (var i = 0; i < this.root.length; i++) {
                context = {root: this.root[i]};
                match = lookup_proxy.call(context, viewName);
                if (match) {
                    return match;
                }
            }
            return null;
        }
        return lookup_proxy.call(this, viewName);
    };
}

module.exports.ViewEnableMultiFolders = ViewEnableMultiFolders;

and used:

var Patch = require('patch.ViewEnableMultiFolders.js');
Patch.ViewEnableMultiFolders(app);
app.set('views', ['./htdocs/views', '/htdocs/tpls']);

Upvotes: 11

user85461
user85461

Reputation: 6640

Here's a solution for Express 3.x. It monkey-patches express 3.x's "View" object to do the same lookup trick as @ShadowCloud's solution above. Unfortunately, the path lookup for the View object is less clean, since 3.x doesn't expose it to express -- so you have to dig into the bowels of node_modules.

function enable_multiple_view_folders() {
    // Monkey-patch express to accept multiple paths for looking up views.
    // this path may change depending on your setup.
    var View = require("./node_modules/express/lib/view"),
        lookup_proxy = View.prototype.lookup;

    View.prototype.lookup = function(viewName) {
        var context, match;
        if (this.root instanceof Array) {
            for (var i = 0; i < this.root.length; i++) {
                context = {root: this.root[i]};
                match = lookup_proxy.call(context, viewName);
                if (match) {
                    return match;
                }
            }
            return null;
        }
        return lookup_proxy.call(this, viewName);
    };
}

enable_multiple_view_folders();

Upvotes: 5

Related Questions