Reputation: 12704
I have a situation where I need to render a Handlebars template created by the user. I've been able to hack together one solution: using a view to compile the user-generated template + context into HTML, then making that resulting string available to the "final" template.
I'd prefer to be able to just expose the template as a variable to the "final" template, and have the final template evaluate it as if it were actual HBS code; but if I use triple braces {{{
, the handlebars variables are (of course) escaped.
Is there another way to do this? A helper perhaps, that first compiles the variable with some context, then outputs the string? The only problem here is that Ember.Handlebars
is wired up to work with Ember; I just need the final, unbound HTML.
Upvotes: 2
Views: 2481
Reputation: 11671
One possible approach could be to use a view
dedicated to rendering the user defined template. Then by setting its template
variable and re-rendering it, the template could change dynamically.
Example,
http://emberjs.jsbin.com/yexizoyi/1/edit
hbs
<script type="text/x-handlebars">
<h2> Welcome to Ember.js</h2>
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="test">
this is the test,
{{view view.userTemplate}}
<button {{action "changeTemplate" 1 target="view"}}>change to Template 1</button>
<button {{action "changeTemplate" 2 target="view"}}>change to Template 2</button>
</script>
js
App.Router.map(function() {
this.route("test");
});
App.IndexRoute = Ember.Route.extend({
beforeModel: function() {
this.transitionTo("test");
}
});
App.UserTemplateView = Ember.View.extend({
template:Ember.Handlebars.compile("initial default template <b>{{view.parentView.parentVar}}</b>")
});
App.TestView = Ember.View.extend({
parentVar:"this is a parent variable",
userTemplate:App.UserTemplateView.create(),
actions:{
changeTemplate:function(templateId){
if(templateId===1){
this.get("userTemplate").set("template",Ember.Handlebars.compile("this is template 1 <b>{{view.parentView.parentVar}}</b>"));
this.get("userTemplate").rerender();
}else{
this.get("userTemplate").set("template",Ember.Handlebars.compile("this is template 2 <b>{{view.parentView.parentVar}}</b>"));
this.get("userTemplate").rerender();
}
}
}
});
This could also be implemented by using a ContainerView
, http://emberjs.com/guides/views/manually-managing-view-hierarchy/
example, http://emberjs.jsbin.com/luzufixi/1/edit
edit - supplement to comments and good solution of Sam Selikoff using a helper
This is one more rough example of using the previous concept, along with the model of a router and the {{view}}
helper which is backed by a generic View
object, i.e. PreviewTemplateView
.
http://emberjs.jsbin.com/tonapaqi/1/edit
http://emberjs.jsbin.com/tonapaqi/1#/test/1
http://emberjs.jsbin.com/tonapaqi/1#/test/2
hbs - calling {{view}}
helper with the desired context that if it contains a template
property, the initial default template will change.
{{view App.PreviewTemplateView contextBinding="this"}}
js
App.Router.map(function() {
this.route("test",{path:"test/:tmpl_id"});
});
App.IndexRoute = Ember.Route.extend({
beforeModel: function() {
this.transitionTo("test",1);
}
});
App.TestRoute = Ember.Route.extend({
model:function(params){
if(params.tmpl_id==1){
return {template:"this is template 1 <b>{{view.parentView.parentVar}},param from context of model:{{someParams.param1}}</b>",someParams:{param1:"p1",param2:"p2"}};
}else{
return {template:"this is template 2 <b>{{view.parentView.parentVar}},param from context of model:{{someParams.param2}}</b>",someParams:{param1:"p1",param2:"p2"}};
}
}
});
App.PreviewTemplateView = Ember.View.extend({
template:Ember.Handlebars.compile("initial default template"),
init:function(){
this._super();
this.refreshTemplate();
},
refreshTemplate:function(){
this.set("template",Ember.Handlebars.compile(this.get("context").get("template")));
this.rerender();
}.observes("context.template")
});
App.TestView = Ember.View.extend({
parentVar:"this is a parent variable"
});
Upvotes: 4
Reputation: 12704
I ended up going with a Handlebars helper. Seems to be a bit more flexible.
I'm using EAK and Coffeescript:
helper = Ember.Handlebars.makeBoundHelper (template, context) ->
dummy = Ember.View.extend
context: context
template: Ember.Handlebars.compile template
view = dummy.create()
$elem = null
Ember.run ->
$elem = $('<div>')
view.appendTo($elem)
return new Handlebars.SafeString $elem.html()
Now in any template I can write
{{preview-template template context}}
where template
is a (possibly user-generated) template, and context
the data.
Upvotes: 2