ericf / express-handlebars

A Handlebars view engine for Express which doesn't suck.
BSD 3-Clause "New" or "Revised" License
2.31k stars 383 forks source link

Inject more data to handlebars during render #101

Closed dominicbarnes closed 9 years ago

dominicbarnes commented 9 years ago

It would be really helpful to add some more meta to options.data before rendering. In particular, I think that layout and view could be useful:

res.render("home", {
  layout: "default"
});
<script src="/public/layouts/{{@layout}}/index.js"></script>
<script src="/public/pages/{{@view}}/index.js"></script>
ericf commented 9 years ago

Hmm…

So I do have some Express Handlebars-specific metadata that could potentially be useful, and it contains the layout name. But for the view name — I'm guessing your expecting view === "home" — rendering engines are only provided the view's file path.

Also, if we do something like this, I think this metadata should be namespaced on the data object so it doesn't clobber any user values, e.g. {{@expressHandlebars.view}} or something [better].

ericf commented 9 years ago

@dominicbarnes I've added support for this in #105. Here is what I'm currently exposing on {{@ExpressHandlebars}}:

Are there any other things you were expecting to be available?

dominicbarnes commented 9 years ago

That sounds great! I was just looking at this last night even, how funny.

I did discover that express.View does keep track of the name it was passed. (see https://github.com/strongloop/express/blob/master/lib/view.js#L43) Unfortunately, that's not available since we're overriding the context. (see https://github.com/ericf/express-handlebars/blob/master/lib/express-handlebars.js#L37)

The point is, it's not that far away and I think it's feasible to try and retrieve it.

ericf commented 9 years ago

What are you thinking for the case where you have two subdirs in views/ and each have an foo.hbs template? Would you want to end up with something like: "about/foo" where the file exists at: "views/about/foo.hbs"?

dominicbarnes commented 9 years ago

Yeah, that's what I was thinking. Whatever value that the user passes to res.render should be available to the template, in fact you could probably pass in both about/foo and /absolute/path/to/views/about/foo.hbs in case people want to do some sort of advanced resolution on their own.

dominicbarnes commented 9 years ago

I haven't figured out exactly how you're assembling the data for the template, but I'm thinking something like:

var self = this;
this.engine = function (viewPath, options, callback) {
  options.view = this;
  self.renderView(viewPath, options, callback);
};

Where renderView needs to be aware of options.view and inject accordingly. :)

I know it's not nearly as clean as before, but it does accomplish the goal without too much extra code. Thoughts?

ericf commented 9 years ago

So Express passes a settings object which contains views, and that's a string path to the views path. The problem is there won't be a way for me to create a string value that's equal to what the user passed to res.render() because all of these will be equivalent:

res.render('home');
res.render('home.hbs');
res.render('./views/home');
res.render('./views/home.hbs');

That said, what I'll do is something similar to how I process the partials and create their names; i.e., "about/foo".

ericf commented 9 years ago

@dominicbarnes okay, added. Check out the commit linked above.

dominicbarnes commented 9 years ago

Sounds good to me! I can't wait to see this merged, I'll be trying it out right away. :)