Rendering HTML with injected Components in HBS templates


#1

Hey friends, I have a problem that is outside of my current knowledge and I was wondering if I could get some insight from the community.

I have a page which renders different HTML depending on user interaction and this HTML is requested from the server as a string.

In the past this string was saved as a property and using HTML.SafeString would be injected into the wrapping template. HTML Example:

<html>
    <body>
        <input/>
    </body>
</html>

Right now we want to add components to this HTML during runtime or on the server. Lets say for simplicity it is from the server. HTML Example:

<html>
    <body>
        {{special-input}}
    </body>
</html>

How should I push this to the wrapping template? SafeString doesn’t work here and just outputs the component as text.

I thought of using hbs-template-inline but am not sure exactly how to do that.

Thanks!


#2

Some answers I got:

alexspeller [6:42 PM] @alon there is no simple way to do that. Mobiledoc does something similar, have a look at how it does it what you are trying to do is not a generally supported use case in ember

sukima [6:44 PM] @alon I would avoid doing that. It seems very prone to abuse. Concider a different render like Markdown or your own markup where you could {{#each}} through the directives and render components with {{component ...}}

tbieniek [6:53 PM] @alon that is not supported anymore afaik

alexspeller [6:49 PM] @alon if you control the full pipeline, mobiledoc is the best solution for this. If you have to render html and inject ember stuff in it, you need something custom. It’s probably going to be a long way from trivial

sukima [10 minutes ago] Oh dear. That is a difficult situation I keep thinking some kind of HTML parser but I do’t think Ember is the right place for that. Might have to abandon Ember in that view component and go all jQuery. You could research hbs-template-inline and maybe it has something but that is diving into some big time Ember voodoo. Personally I think I would investigate converting the HTML into an Ember template parsable format like a JSON object of some kind. Then use that to render recursive components in Ember letting the user enter data. Then on the server convert the JSON to HTML for the email.


#3

Do you control the server? Are these template strings trusted or are they the result of user input?


#4

I control the server. These HTML strings are built mostly by our own developers but are sometimes from other sources. They can all be approved and vetted before use though so they can be trusted.


#5

Any reason not to store the precompiled template alongside the raw string in the database? There are some downsides with this approach, but the upsides are simplicity and “it just works”…


#6

Do you mean storing the hbs template in the database? I could do this. How would I then go about displaying this at runtime? Thanks!


#7

Essentially, you can do something like this on the server:

const compiler = require('ember-source/dist/ember-template-compiler'); // we should really make this better :(
let handlebarsString = `<p>Hello World!</p>`; 
let compiledTemplate = compiler.precompile(handlebarsString);
// returns a string that is suitable to save directly in the database
// => "{"id":null,"block":"{\"symbols\":[],\"statements\":[[7,\"p\"],[9],[0,\"Hello World!\"],[10]],\"hasEval\":false}","meta":{}}"

Then client side, you would take the precompiled string and register it under an appropriate component’s name:

function registerTemplate(owner, componentName, precompiledTemplate) {
  owner.register(
    `template:components/${componentName}`,
    Ember.HTMLBars.template(precompiledTemplate)
  );
}

And now you could use that in your templates via something like this:

{{component nameOfComponentRegistered}}

The above is roughly the “howto”, but please make sure to validate these templates before ever storing the compiled template output. Used incorrectly, the above “howto” could result in severe breaches of data…