Partials with Node.js + Express + Hogan.js

2020-02-08 07:33发布

问题:

I'm developing a site with Node.js + Express and using as view engine Hogan.js.

This is my file app.js:

/**
 * Module dependencies.
 */

var express = require('express')
  , routes = require('./routes')
  , user = require('./routes/user')
  , http = require('http')
  , path = require('path');

var app = express();

app.configure(function(){
  app.set('port', process.env.PORT || 3000);
  app.set('views', __dirname + '/views');
  app.set('view engine', 'hjs');
  app.use(express.favicon());
  app.use(express.logger('dev'));
  app.use(express.bodyParser());
  app.use(express.methodOverride());
  app.use(express.cookieParser('your secret here'));
  app.use(express.session());
  app.use(app.router);
  app.use(require('less-middleware')({ src: __dirname + '/public' }));
  app.use(express.static(path.join(__dirname, 'public')));
});

app.configure('development', function(){
  app.use(express.errorHandler());
});

app.get('/', routes.index);
app.get('/about', routes.about);
app.get('/users', user.list);

http.createServer(app).listen(app.get('port'), function(){
  console.log("Express server listening on port " + app.get('port'));
});

The file /routes/index.js is:

/*
 * GET pages.
 */

exports.index = function(req, res){
  res.render(
    'index',
    {
      title: 'Home Page',
      author: 'Bruce Wayne'
    }
  );
};

exports.about = function(req, res){
  res.render(
    'about',
    {
      title: 'About Page',
      author: 'Bruce Wayne'
    }
  );
};

In /views folder, there are:

|- part.hjs

|- index.hjs

|- cv.hjs

The file part.hjs is:

<h3>Hello {{ author }}</h3>

The file index.hjs is:

<h1>Title: {{ title }} </h1>
{{> part }}
Welcome to Gotham City.

And the file about.hjs is:

<h1>Title: {{ title }}</h1>
{{> part }}
I'm not Joker.

I've two questions:

  1. How can I use properly the partials in my pages? (this code doesn't work)
  2. Can I use the same "title" for two or more pages without repeat the values assignment in file /routes/index.js?

Best regards, Vi.

回答1:

I've found a solution for the first question.

First of all, I removed hjs:

npm remove hjs

Then, I installed the package hogan-express:

npm install hogan-express

Furthermore, I edited app.js:

/**
 * Module dependencies.
 */

var express = require('express')
  , routes = require('./routes')
  , user = require('./routes/user')
  , http = require('http')
  , path = require('path');

var app = express();

app.engine('html', require('hogan-express'));
app.enable('view cache');

app.configure(function(){
  app.set('port', process.env.PORT || 3000);
  app.set('views', __dirname + '/views');
  app.set('view engine', 'html');
  app.use(express.favicon());
  app.use(express.logger('dev'));
  app.use(express.bodyParser());
  app.use(express.methodOverride());
  app.use(express.cookieParser('your secret here'));
  app.use(express.session());
  app.use(app.router);
  app.use(require('less-middleware')({ src: __dirname + '/public' }));
  app.use(express.static(path.join(__dirname, 'public')));
});

app.configure('development', function(){
  app.use(express.errorHandler());
});

app.get('/', routes.index);
app.get('/users', user.list);

http.createServer(app).listen(app.get('port'), function(){
  console.log("Express server listening on port " + app.get('port'));
});

And routes/index.js:

exports.index = function(req, res) {
  res.locals = {
    title: 'Title',
  };
  return res.render(
    'index',
    {
      partials:
      {
        part: 'part',
      }
    }
  );
};

Now, in /views there are index.html, part.html. The file part.html contains:

<h1>{{ title }}</h1>

The file index.html contains:

{{> part}}
Hello world!

So, It works fine.



回答2:

At least in Express 4+, partials just work out of the box. You can use express-generator (from npm) with --hogan or -H option.

After doing that, you need to add partials to the render method:

router.get('/', function(req, res, next) {
  res.render('index', 
        { 
            title: 'My Site',
            partials: {header: 'header'} 
        });
});

Then, in your template use {{ > xxx }}

<body>
  {{> header }}
  <h1>{{ title }}</h1>

  <p>Welcome to {{ title }}</p>
</body>

NOTE: this has header.hjs in views



回答3:

To use partials with express+hogan, just do the following:

app.get('/yourRoute', function(req, res){  
   res.render('yourPartial', function(err,html){
       var partialHTML = html;
       res.render('yourMainView', { myPartial: partialHTML }, function(err,html){
          res.send(html);   
       });     
   });
}

And now, yourMainView.html:

<p>Something Something Something</p>
{{{partialHTML}}}
<p>Bla Bla Bla</p>

Notice the triple '{' instead of double as you usually do! That telling hogan (mustache) to parse this as HTML rather then a string!

That's it.



回答4:

As for your partials question, if you use consolidate.js you can simply do:

res.render('index', {
  partials: {
    part  : 'path/to/part'
  }
});


回答5:

This is a problem. Partial support is difficult to come by in Express 3.

Your best bet is: https://github.com/visionmedia/consolidate.js npm install consolidate

These patches take different approaches to adding partials for Hogan:

  • https://github.com/visionmedia/consolidate.js/pull/51
  • https://github.com/visionmedia/consolidate.js/pull/29

Unfortunately, the engine doesn't have a hook for filesystem based partials natively, so I think people are confused about how and where partials should be implemented. I ended up with LinkedIn's Dust.js implementation, since partial support was already there. Master actually has even better support, plus I submitted a patch yesterday for relative paths.

Josh



回答6:

I would use mmm instead of hjs.

https://github.com/techhead/mmm

Disclaimer: I wrote the package.

Just replace all occurrences of hjs with mmm and partials will start working. There is a lot more information and an example at the link above.

As for your other question, if you want to share properties across multiple views, you have a couple of options.

When you call res.render(name, options), the options will actually be merged onto res.locals and app.locals before being passed to the rendering engine. Therefore to set an app-wide property, you can simply assign it to app.locals.

app.locals.title = "Default Title"; // Sets the default title for the application

This concept really applies to just about any Express 3 View Engine.

However, for mmm specifically, please see the section under Presentation Logic for more ways to bind values to a template or set of templates.