ericf / express-handlebars

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

Access to `Handlebars.partials` inside global helpers file #89

Closed tuxsudo closed 9 years ago

tuxsudo commented 9 years ago

We want to be able to render partials from inside helpers. For example, in our global helpers file, when we try something like:

var Handlebars = require('handlebars');

module.exports = {
    something: function(options) {
        var partial = Handlebars.partials['partial-name'];
              html = partial(options.hash);
        //....... 
    }
}

That partial partial-name is not available because Handlebars.partials is an empty object {}.

What is the best way to create/import/inject the Handlebars.partials object (as defined in the partialsDir option) so our helpers can instantiate them?

Thanks in advance!

ericf commented 9 years ago

The partials won't be available on Handlebars.partials unless you explicitly register them there as this package does not want to mess around with the global partials registry to avoid conflicts from other code running in the process.

That said, I think they should be accessible via options.partials. Can you see if they're there?

ericf commented 9 years ago

Also note that they are named with path-like syntax. So if you have multiple nested dirs of partials their name will be something like: "user/avatar", which corresponds to views/partials/user/avatar.hbs.

tuxsudo commented 9 years ago

That said, I think they should be accessible via options.partials. Can you see if they're there?

By options.partials, do you mean the same helper options object where you typically find options.hash and options.fn (for block helpers)? If so, it's not there...

ericf commented 9 years ago

Yeah you're right they're not there; bummer.

The quickest way to hack this in would be to override the private _renderTemplate() method.

var app = express(),
    hbs = exphbs.create({ /* config */ });

// Register `hbs.engine` with the Express app.
app.engine('handlebars', hbs.engine);
app.set('view engine', 'handlebars');

// Override private `_renderTemplate()` method to expose partials.
hbs._renderTemplate = function (template, context, options) {
    // Expose partials on `options.data.partials` in helpers.
    options.data || (options.data = {});
    options.data.partials = options.partials;

    template(context, options);
};

Now inside your helpers, you can access the partials via options.data.partials.

Let me know if that if this fixes your issue. And I'll think more about how to integrate this into this package without requiring the hack above. I'm also interested to see if anyone else is wanting to do a similar thing with invoking partials via helpers…

tuxsudo commented 9 years ago

We want to use Handlebars both on the server and the client interchangeably and/or simultaneously and use it the same way. On the clientside, there is the global Handlebars object which knows all the helpers & partials; we want to mimic that functionality serverside.

I ended up doing something like the following to make a 'Global' like Handlebars Object where the Handlebars' helpers are aware of the partials:

var Handlebars = require('handlebars'),

    ExpressHandleBars = require('express-handlebars').create({
        layoutsDir: "views/layouts/",
        partialsDir: "views/partials/"
        handlebars: Handlebars
    });

ExpressHandleBars.getPartials().then(function (partials) {
    Handlebars.partials = partials;
    var helpers = require("./views/helpers")( Handlebars );
    Object.keys(helpers).forEach(function(helper) {
        Handlebars.registerHelper(helper, helpers[helper] );
    });
});

The helpers file (./views/helpers) looks something like:

module.exports = function(Handlebars) {

    return {

        "helper": function (options) {
            // The `Handlebars` object now acts "global" and with knowledge of all partials and helpers.

        }
    }

Still feels somewhat hacky, but it works. Thoughts?

ericf commented 9 years ago

I'm already referencing any helpers from Handlebars.helpers and mixing them with and local or per-render helpers that are specified.

I'm willing to entertain the idea of doing something similar for partials, but it would be up to you to globally register partials with Handlebars since that's something this package won't be doing in order to maintain a sandbox.

tuxsudo commented 9 years ago

That would be awesome. :)

ericf commented 9 years ago

@TuxSudo also if you're up for it, feel free to open a PR to implement this feature :smile:

ericf commented 9 years ago

This seems very much related to #101, and in PR #105 I'm now passing along the partials to the data channel during rendering making them available inside of helpers via options.data.ExpressHandlebars.partials.

Let me know if you think that will solve what you're after here, and if so, I'll close this out once #105 is merged.

tuxsudo commented 9 years ago

Not particularly, but that's ok. The goal is to allow development of helpers and partials that work both server and clientside. Clientside, handlebars puts partials at Handlebars.partials. So, I'd still have to register them globally anyway with the proposed solution. But I will close this and potentially submit a pull request later.

Thanks for your work on this. :+1:

rahil051 commented 8 years ago

@tuxsudo thankyou for the hint. I was wondering how can I access Handlebars instance in my helpers file when using express-handlebars package.

this is the hack I used

// app.js

var hbs = exhbs.create({
    defaultLayout: 'main',
    helpers: require('./lib/hbs-helpers'),
    // other options
});

and in lib/hbs-helpers.js

Handlebars = require('handlebars'); // the official handlebars package
module.exports = (function(Handlebars) {
   fallback: function(actual, defaultValue) {
       var out = actual || defaultVal;
       return new Handlebars.SafeString(out); // now I can access Handlebars instance
   }
})(Handlebars);

This is a hack as I am not good with documentations so is there an integration with express-handlebars.