简体   繁体   中英

How to do multi-render with Node.js, EJS Template and dynamic file name

I use Node.js with Express and EJS Template and I want manage my HTML Templace dynamically..

For this time, I have :

- server.js
- views/
- - template.ejs
- - layout.ejs 

In my server.js, i load the templates view, with data, and I use <% include %> to load layout on the template :

server.js

var data = { some: 'stuff'};
res.render('template', data);

template.ejs

<% include layout %>

At this tine, the code work very good and I have the expected result.

But now, I want a variable on data, to say the filename of the layout, available on a layouts/ directory :

- server.js
- views/
- - template.ejs
- - layouts/
- - - home.ejs 
- - - login.ejs 

server.js

var data = { 
   layout_view: 'layouts/home'
};
res.render(template, data);

template.ejs

<% include layout_view %>

But, variable are not expored and only the name of the variable are tested, not the value :

Error: ENOENT, no such file or directory 'C:\\Users\\Arthur\\Desktop\\Node-www\\G\\views\\layout_view.ejs

How can load the view 'views/layouts/home.ejs' with the <% include %> command and a variable ?!

Thank you all ! :)

Here's what I use for layouts in Express 4 (tested with ejs):

/*
   Usage:
     Set a global/default layout with:
        app.set('view layout', 'bar');
     Set a layout per-render (overrides global layout) with:
        res.render('foo', { layout: 'bar' });
     Or disable a layout if a global layout is set with:
        res.render('foo', { layout: false });
     If no layout is provided using either of the above methods,
     then the view will be rendered as-is like normal.

     Inside your layout, the variable `body` holds the rendered partial/child view.

   Installation:
     Call `mpResponse();` before doing `require('express');` in your application.
*/

function mpResponse() {
  var expressResponse = require('express/lib/response'),
      expressResRender = expressResponse.render;
  expressResponse.render = function(view, options, fn) {
    options = options || {};
    var self = this,
        req = this.req,
        app = req.app,
        layout,
        cb;

    // support callback function as second arg
    if (typeof options === 'function')
      fn = options, options = {};

    // merge res.locals
    options._locals = self.locals;

    // default callback to respond
    fn = fn || function(err, str) {
      if (err) return req.next(err);
      self.send(str);
    };

    if (typeof options.layout === 'string')
      layout = options.layout;
    else if (options.layout !== false
             && typeof app.get('view layout') === 'string')
      layout = app.get('view layout');

    if (layout) {
      cb = function(err, str) {
        if (err) return req.next(err);
        options.body = str;
        expressResRender.call(self, layout, options, fn);
      };
    } else
      cb = fn;

    // render
    app.render(view, options, cb);
  };
}

Resolved on comment by Ben Fortune :

Function not available yet on EJS, so update your files ejs.js and lib/ejs.js (ligne ~155)

Old code

  if (0 == js.trim().indexOf('include')) {
    var name = js.trim().slice(7).trim();
    if (!filename) throw new Error('filename option is required for includes');
    var path = resolveInclude(name, filename);
    include = read(path, 'utf8');
    include = exports.parse(include, { filename: path, _with: false, open: open, close: close, compileDebug: compileDebug });
    buf.push("' + (function(){" + include + "})() + '");
    js = '';
  }

New code

  if (0 == js.trim().indexOf('include')) {
    var name = js.trim().slice(7).trim();
    if (!filename) throw new Error('filename option is required for includes');
    // If it is not path, but variable name (Added)
    var resolvedObj = null;
    var objName = name.split(".");
    if (objName) {
       resolvedObj = options;
       for (var idx=0; idx<objName.length; idx++) {
           resolvedObj = resolvedObj[objName[idx]];
           if ( ! resolvedObj) {break;}
       }
    }

    if(resolvedObj)
       var path = resolveInclude(resolvedObj, filename);
    else
       var path = resolveInclude(name, filename);
    include = read(path, 'utf8');
    include = exports.parse(include, options); // Added transfer whole options
    buf += "' + (function(){" + include + "})() + '";
    js = '';
  }

Sources

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM