简体   繁体   中英

How do I load different partials dynamically using handlebars templates?

I'm loading a template with the following data:

"slides": [
    {
        "template": "video",
        "data": {
            "video": ""
        }
    },
    {
        "template": "image",
        "data": {
            "image": ""
        }
     }
]

in my template I want to loop over these slides and based on the configured template I want to load a partial

{{#each slides}}
    {{> resources_templates_overlay_video }}
{{/each}}

How can I make this partial load dynamically (based on the configured template)?

I'm using the require-handlebars-plugin

As far as I can tell, hbs expects the partials to be known at compile time, which is way before you pass in your data. Let's work around that.

First, pull in your dynamic partials before rendering, something like:

// I required the main template here for simplicity, but it can be anywhere
var templates = ['hbs!resources/templates/maintemplate'], l = data.slides.length;
for (var i=0; i<l; i++ )
    templates.push('hbs!resources/templates/overlay/'+data[i].template);

require(templates, function(template) {
    var html = template(data);
});

And define a helper that will act as a dynamic partial

define(['Handlebars'], function (Handlebars) {

    function dynamictemplate(template, context, opts) {
        template = template.replace(/\//g, '_');
        var f = Handlebars.partials[template];
        if (!f) {
            return "Partial not loaded";
        }

        return new Handlebars.SafeString(f(context));
    }

    Handlebars.registerHelper('dynamictemplate', dynamictemplate);
    return dynamictemplate;
});

Finally, modify your main template to look like

{{#each slides}}
    {{dynamictemplate this.template this.data}}
{{/each}}

I found the above answers a little hard to understand - they leak globals, have single character variables, and some odd naming. So here's my own answer, for my (and your) reference:

A dynamic partial using 'hbs', express.js default handlebars implementation :

I used this to make a simple blog making (article-name).md into /blog/(article-name) , creating a dynamic partial:

// Create handlebars partials for each blog item
fs.readdirSync('blog').forEach(function(blogItem){
    var slug = blogItem.replace('.md','')
    var fileContents = fs.readFileSync('blog/'+blogItem, 'utf8')
    var html = marked(fileContents)
    var compiledTemplate = hbs.compile(html);
    hbs.registerPartial(slug, compiledTemplate);
})

// Create 'showBlogItem' helper that acts as a dynamic partial
hbs.registerHelper('showBlogItem', function(slug, context, opts) {
    var loadedPartial = hbs.handlebars.partials[slug];
    return new hbs.handlebars.SafeString(loadedPartial(context));
});

Here's the route. It 404s if the partial doesn't exist, because the blog doesn't exist.

router.get('/blog/:slug', function(req, res){
    var slug = req.param("slug")
    var loadedPartial = hbs.handlebars.partials[slug];
    if ( ! loadedPartial ) {
        return res.status(404).json({ error: 'Article not found' })
    }
    res.render('blog', {
        slug: slug
    });
})

/views/blog.hbs looks like:

<div class="blog">
    {{ showBlogItem slug }}
</div>

When Handlebars.partials[] returns a raw string it means the partial is not compiled.

I am not sure but my best guess is that Handlebars compiles the partial internally when it compiles the template which includes the partial. So when you use a helper to include a partial then Handlebars doesn't recognize it and it will not be compiled.

You can compile the partial yourself. Don't forget to register the compiled partial or you end up compiling every time the partial is required, which hurts performance. Something like this should work.

var template = Handlebars.partials['templatename'],
    fnTemplate = null;

if (typeof template === 'function') {
  fnTemplate = template;
} else {
  // Compile the partial
  fnTemplate = Handlebars.compile(partial);
  // Register the compiled partial
  Handlebars.registerPartial('templatename', fnTemplate);  
}

return fnTemplate(context);

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