Search code examples
ember.jshandlebars.js

Render ember.js .hbs template to a string


I currently can do the following:

var template = Handlebars.compile("Hello <em>{{name}}</em>!");
var html = template({ name: 'World' });

The html variable now holds the string "Hello <em>World</em>!".

But how can I do the same with a template whose code is in a .hbs file inside my app/templates directory?

(I'm using ember-cli with ember 1.9.1)


Solution

  • In Ember 1.13.15 this will work (UPDATE to work with v2.4 at the bottom):

    // template-to-string.js
    import Ember from 'ember';
    
    export default function(container, template, context) {
      let resolvedTemplate =
        typeof template === 'string' ? container.lookup('template:' + template) : template;
      context = context || {};
    
      Ember.assert(`Template '${template}' could not be resolved`, !!template);
    
      return new Ember.RSVP.Promise((resolve) => {
    
        Ember.Component.create(Ember.merge({
          style: 'display:none;',
          layout: resolvedTemplate,
          container,
    
          didRender() {
            resolve(this.$().html());
            this.destroy();
          }
        }, context)).append();
    
      });
    }
    

    It's based on a solution proposed in this post: How to Render an HTMLBars Template to a String in Ember 2.0? where further explanation is offered.

    I have extended to support passing in a template context.

    Use the function like so:

    // my-component.js
    import Ember from 'ember';
    import templateToString from './template-to-string';
    
    export default Ember.Component.extend({
    
      _appendMyChildTemplate() {
        let myTemplateContext = ...;
    
        templateToString(this.container, '<my-template-path>', myTemplateContext)
         .then((html) => {
           this.$().append(html);
         });
      }
    
    })
    

    Update to work in Ember 2.4:

    I can't claim to fully understand this or that it is anywhere near the best solution, but it works :)

    Not massively different from the function above:

    import Ember from 'ember';
    
    export default function(container, template, context) {
      let resolvedTemplate =
        typeof template === 'string' ? container.lookup('template:' + template) : template;
      context = context || {};
    
      Ember.assert(`Template '${template}' could not be resolved`, !!template);
    
      return new Ember.RSVP.Promise((resolve) => {
    
        Ember.Component.extend(Ember.merge({
          style: 'display:none;',
          layout: resolvedTemplate,
          container,
    
          init() {
            this._super.apply(this, arguments);
            Ember.setOwner(this, container);
          },
    
          didRender() {
            resolve(this.$().html());
            this.destroy();
          }
        }, context))
          .create()
          .append();
    
      });
    }