Re-render handlebars template after ajax response

2019-06-09 04:38发布

问题:

In my handlebars template I have a div to display flash messages like this

        <div id="flashContainer" class="alert alert-dismissible alert-{{flash.type}}" style="display: {{#if flash}}block {{else}}none{{/if}}">
                <button type="button" class="close"
                        data-dismiss="alert" aria-hidden="true">&times;</button>
                    <strong>{{flash.intro}}</strong> {{{flash.message}}}
            </div>

After submitting a form with an ajax request I want to set the flash message and then if the request is successful I want to update only this part of the page. However I don't want to manually update the html but I want to re-render the part of the template shown above. How can I achieve this ? I'm using handlebars and expressJS.

回答1:

You could separate your ajax response template and render that only after the response.

However, I'm guessing you want to keep it all in one template and append it to the page only once. Handlebars doesn't natively support this since it aims to create logic-less templates and deals with strings instead of DOM elements. You can do this with a helper using Handlebars but there are also plenty of other popular templating solutions that might do the trick for you, see for example the google search data binding template.

If you want to do it with Handlebars, I've created a helper you can use. See post-render-bars and its renderer helper. Here's a demo for how to use it to render an ajax response.

The helper uses MutationObserver, here's browser support info, see webcomponents-lite for a polyfill if needed.

Brief description for the relevant code:

watch.js defines a function forHtml(html, callback) which triggers a callback when the given html is encountered in the DOM. It modifies the html to temporarily have a class that makes it unique.

helpers.js defines the helper renderer and the function createRenderer(getHtmlContentFn) which wraps your function into a renderer and that can be passed in to a template and used as an argument for the helper.

The helper uses the watch.forHtml function to watch for the block defined by the helper and ties that together with the renderer. Whenever the renderer function is called the relevant DOM element gets updated.

Here's a shortened version of the example I linked, template:

<p>Ajax response below:</p>
{{#renderer response}}<div>Loading...</div>{{/renderer}}

And js:

var context = {
    response: postHandlebars.createRenderer(function(data) {
        return data;
    });
};
var html = template(context);
// append html somewhere to see the response

$.ajax({
    type: 'GET', 
    url: 'some url'
}).done(context.response);