Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how can I invoke an ember component dynamically via a variable?

Lets say I have an array of widget objects on my controller and each widget object has member variable that is assigned the name of a component class. How can I get my template to invoke that component?

//widgets[0].widget.componentClass="blog-post"

{{#each widget in widgets}}
    {{widget.componentClass}}
{{/each}}

Obviously the above example just spits out a series of string versions of the widget component classes. This however does work (as long as you got everything set up right):

//widgets[0].widgets.viewClass="blogPost"

{{#each widget in widgets}}
    {{view widget.viewClass}}
{{/each}

That was our previous implementation, but we weren't happy with it. We're currently using a custom {{renderWidget ...}} tag with a handlebars helper as described here: Calling Handlebars {{render}} with a variable name. The default render helper has a similar problem where it would not invoke a render on the contents of a variable name. I'd be willing to write a custom component handlebars helper but I can't even figure out where to start. Thanks.

like image 214
StrangeLooper Avatar asked Sep 24 '13 02:09

StrangeLooper


4 Answers

I tried this and it seems to work, but its just a lot of guesswork on my part:

Ember.Handlebars.registerHelper('renderComponent', function(componentPath, options) {
  var component = Ember.Handlebars.get(this, componentPath, options),
      helper = Ember.Handlebars.resolveHelper(options.data.view.container, component);

  helper.call(this, options);

});

and you use it the same way:

{{#each widget in widgets}}
  {{renderComponent widget.componentClass widget=widget}}
{{/each}}
like image 98
StrangeLooper Avatar answered Nov 02 '22 06:11

StrangeLooper


In Ember 1.11, the new component helper allows you to do this:

{{#each widget in widgets}}
  {{component widget.componentClass}}
{{/each}}

As of today, Jan 19th, 2015, 1.11 is not a stable release but this feature is in the canary version.

like image 23
wbyoung Avatar answered Nov 02 '22 05:11

wbyoung


Sounds like I've run into a lot of the same problems as you. All components are registered as top level helpers, which means you can do a similar method to the one you linked of creating a handlebars helper that does the lookup. Like this:

Ember.Handlebars.registerHelper('lookup', function(component, options) {
  component = Ember.Handlebars.get(this, component, options);
  Ember.Handlebars.helpers[component].call(this, options);
});

Then in your template:

{{#each widget in widgets}}
  {{lookup widget.componentClass}}
{{/each}}

Here's a jsbin with a working example: http://jsbin.com/ucanam/2482/edit

Hope that helps!

-- edit --

For some reason, the new version of handlebars makes calling helpers from other helpers impossible. Instead you can lookup the template through the Ember.TEMPLATES global. I've updated the JSBin to use this method.

You can also get the template via options.data.view.templateForName(component), but it feels a bit more brittle than Ember.TEMPLATES.

-- edit 2 --

It's changed again. Ember.Handlebars.resolveHelper is now the correct way to do it. See @StrangeLooper's answer.

like image 21
zaius Avatar answered Nov 02 '22 06:11

zaius


If you are using Ember CLI and Coffeescript here is a version for that. Create the following file in app/helpers/render-component.coffee:

renderComponent = (componentPath, options)->
  helper = Ember.Handlebars.resolveHelper(options.data.view.container, componentPath)
  helper.call this, options

`export { renderComponent }`
`export default Ember.Handlebars.makeBoundHelper(renderComponent)`

From there, you can call {{render-component "foo-bar"}} from a template.

Since the Ember ecosystem is ever changing, here is the version I tested it on:

  • Ember-CLI v0.0.43
  • Ember v1.7.0
  • Handlebars 1.3.0
like image 2
brettish Avatar answered Nov 02 '22 06:11

brettish