ericf / express-handlebars

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

Including partials dynamically #121

Closed justinledouxweb closed 8 years ago

justinledouxweb commented 9 years ago

Hi,

I have been looking for a simple way to dynamically render partials based on a value in an {{#each ...}} loop.

For example:

{{#each modules}}
    {{> @exphbs.partials[ this.name ] }}
{{/each}}

This would allow me to load partials based on data rather than putting a lot of logic in the template.

ericf commented 9 years ago

Dynamic partials are supported in Handlebars 3.0. See: https://github.com/wycats/handlebars.js/pull/941

So you would do:

{{#each modules}}
    {{> (this.name) }}
{{/each}}
justinledouxweb commented 9 years ago

@ericf I must be missing something then...

TypeError: undefined is not a function
   at Object.eval (eval at <anonymous> (/Users/justinledoux/Documents/node/qsPortal/node_modules/express-handlebars/node_modules/handlebars/dist/cjs/handlebars/compiler/javascript-compiler.js:209:23), <anonymous>:5:90)
   at prog (/Users/justinledoux/Documents/node/qsPortal/node_modules/express-handlebars/node_modules/handlebars/dist/cjs/handlebars/runtime.js:178:15)
   at execIteration (/Users/justinledoux/Documents/node/qsPortal/node_modules/express-handlebars/node_modules/handlebars/dist/cjs/handlebars/base.js:133:19)
   at Object.<anonymous> (/Users/justinledoux/Documents/node/qsPortal/node_modules/express-handlebars/node_modules/handlebars/dist/cjs/handlebars/base.js:142:11)
   at Object.eval (eval at <anonymous> (/Users/justinledoux/Documents/node/qsPortal/node_modules/express-handlebars/node_modules/handlebars/dist/cjs/handlebars/compiler/javascript-compiler.js:209:23), <anonymous>:5:34)
   at ret (/Users/justinledoux/Documents/node/qsPortal/node_modules/express-handlebars/node_modules/handlebars/dist/cjs/handlebars/runtime.js:144:30)
   at ret (/Users/justinledoux/Documents/node/qsPortal/node_modules/express-handlebars/node_modules/handlebars/dist/cjs/handlebars/compiler/compiler.js:462:21)
   at ExpressHandlebars._renderTemplate (/Users/justinledoux/Documents/node/qsPortal/node_modules/express-handlebars/lib/express-handlebars.js:247:12)
   at ExpressHandlebars.<anonymous> (/Users/justinledoux/Documents/node/qsPortal/node_modules/express-handlebars/lib/express-handlebars.js:173:21)

This is what I have in the code:

{{#each portal.modules}}
    {{> (this.url) }}
{{/each}}

(portal is a res.locals variable available on the request to the page I am hitting).

If I print {{this.url}} I am getting the proper values (alerts, mapping, news).

ericf commented 9 years ago

So I didn't try to the code myself… let me know what you figure out.

ericf commented 9 years ago

Looking at the unit tests the subexpression needs to be a helper. Something like this:

var app = express();
app.set('view engine', 'hbs');
app.engine('hbs', exphbs({
    helpers: {
        partial: function (name) {
            return name;
        }
    }
}));
{{#each modules}}
    {{> (partial this.name) }}
{{/each}}
samuelfine commented 8 years ago

Hi Justin! I took a look at this, and it appears to be a quirk of how Handlebars processes subexpressions:

"Subexpressions do not resolve variables so whichPartial must be a function."

In your code, this.url is (presumably) a string, when it needs to be a function returning a string.

modules: [
  { url: '...' },
  { url: '...'}
]

needs to be:

modules: [
  { url: function() { return '...'; } },
  { url: function() { return '...'; } },
]

Kinda awkward. Alternatively, you could perform a lookup on the value, like so:

{{#each portal.modules}}
    {{> ( lookup . 'this.url') }}
{{/each}}

I've tested both these approaches in v.2.0.1 of express-handlebars, and both work without any custom helpers required. I'm using lookup in my own code, since it doesn't require polluting an object with a ton of anonymous functions. :smile:

ghost commented 8 years ago

Apparently your dynamic partials need to parsed as a function which returns a string.

flyingL123 commented 8 years ago

I'm having trouble with dynamic partials as well. I thought I was doing it correctly but I keep receiving a partial not found error. Oddly, the error disappears if I mention the partial in a commented handlebars statement, as if that commented statement loaded the template, so now the dynamic loading works. Please see the details here:

http://stackoverflow.com/questions/36438434/handlebars-and-node-dynamic-partial-not-found

Can you anyone help?

sidonaldson commented 6 years ago

If you're using the handlebar-helpers library you can use a standard string function such as lowercase. It might not do anything but it returns a function and fixes this niggle.

{{> (lowercase this.url)}}