EmberJS registerHelper to dynamically render a template in Ember 1.8


#1

I’d like to dynamically render a template, like this:

{{#each layout in layouts}}
   {{render layout.get('type') layout}}
{{/each}} 

The problem is that render doesn’t expect a variable as its first arguments, so in the older EmberJS-versions, a workaround was possible by registering a helper:

Ember.Handlebars.registerBoundHelper('render_layout', 
  function(callingContext, layout, options) {
    return Ember.Handlebars.helpers.render.call(callingContext, 
                layout.get('type'), 'layout', options);
});

and in the template:

{{#each layout in layouts}}
     {{render_layout this layout}}
{{/each}} 

Unfortunately the thing doesn’t work in newer ember versions. Ember.Handlebars.helpers.render.call expects 3 arguments but I can not get them right. Any ideas? I’ve tried: (this, layout.get('type'), options)

and in the template:

{{#each layout in layouts}}
     {{render_layout layout}}
{{/each}} 

I get Uncaught TypeError: Cannot read property 'pushChildView' of null

… and many others.

Any suggestions would be greatly appreciated.

at stackoverflow: /questions/27779572/emberjs-registerhelper-to-dynamically-render-a-template-in-ember-1-8


#2

If all you’re trying to do is dynamically render a template, the partial helper should handle this just fine.

The render helper does a lot little more than just render a template and isn’t data bound like {{partial}} and {{view}}.


#3

Hey, I was actually trying to do something else. See the stackoverflow thread


#4

Hi @lustoykov
Any luck? I’ve been trying to achieve something like what you were looking for here and I’m still unable to properly render my views. I wanna render my templates dynamically (within a #each loop) and keep them alive in the DOM. the problem is that their contexts are not properly setup (controllers to be specific).

Thanks


#5

Hey inexuscore,

I’d say my approach was incorrect “by design”. If you really want to achieve something like this, your probably need to render a component and pass it a model or/and a controller

Take a look here: stackoverflow: /questions/27779572/emberjs-registerhelper-to-dynamically-render-a-template-in-ember-1-8

What I’m doing now is render a component, while passing a model. Based on the model, I assign the component’s template.

Like this:

Admin.RenderElementComponent= Ember.Component.extend

    tagName: "li"
    classNames: ["element-instance"]
    layoutName: (->
        @get "displayElement.type"
    ).property "displayElement.type"

In the template:

<ul class = "sortable-elements">
        {{#each element in elements_container}}
            {{render-element displayElement=element}}
        {{/each}}
 </ul>

So, layout is assigned based on the “type” of my elements - video, picture, etc…

Does it help?


#6

Hey @lustoykov

Thanks for your post, I’ve tried this solution before. It works fine but this isn’t exactly what I’m looking for. Would you kindly take a look this demo repo that I’ve created: inexuscore/ember-tabbed-ui

If i render my templates with a component helper, then I’ll have isolated views. meaning the rendered content will have the component controller as its controller. Is there any way to use my own objects instead? perhaps i could assign my own view/controller manually.

If you look at the source code on my demo repo you’ll see that I’m rendering my templates inside a {{#each}} loop. the only problem i have is that my controllers (context) don’t get assigned properly, if at all!

Thanks for your time


#7

Hi inexuscore,

right, that’s an issue. The idea behind the components is exactly using them also as controllers. But still, you could send actions to the template’s controller. What I do:

Admin.RenderElementComponent= Ember.Component.extend

    tagName: "li"
    classNames: ["element-instance"]
    layoutName: (->
        @get "displayElement.type"
    ).property "displayElement.type"

    ....     

    click: ->
        
        @set "action", "elem_selected"
        @sendAction "action", @get "displayElement"

See, that’s how I communicate with the controller. I think you could also use something like that:

As a property in the RenderElementComponent - templates_controller: Ember.computed.alias "parentView.controller"

and then in handlebars {{action "test" target = templates_controller}}

On the other hand, the templates_controller could access your custom-defined controllers in a needs property. So you could also target them: in handlebars {{action "test" target = templates_controller.controllers.another_controller}}

If that’s too cumbersome, you could use the view helper instead where you get automatically the template’s controller assigned to the view. Remember, here you could also assign a model and have the layoutName as a computed property. Then you could do the trick with the needs and another controllers. What the view saves you is the "target = templates_controller" in the action and the alias property.

I’m not an expert, have 3-4 months experience in Ember, but that’s how I’ve organised our project. However, it scales well for now.

BTW. Ive just seen that I’ve assigned dynamically controllers in one of my components as an attribute, so that’s also possible. Actually I think that’s what you’re looking for. Simply assign the controller as a property to the component, it should work. Sorry that I didn’t take a look at your code. I think you tried to do this anyway. But it works, probably you have a minor bug or so.


#8

In Ember 1.10 you can define a dynamic HTMLBars render helper like this:

import Ember from 'ember';

export default Ember.HTMLBars._registerHelper('render-dynamic', function(params, hash, options, env) {
    var model = this.get('context');
    // determine template/view string
    var type = model.get('type');
    params[0] = type;

   // pass modified parameters to underlying Ember render helper
    this.container.resolve('helper:render').helperFunction.call(this, params, hash, options, env);
});

You will have to pass a dummy string as second argument:

{{ render-dynamic 'x' model }}