EmberJS registerHelper to dynamically render a template in Ember 1.8 - ember.js

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.

Components allow you to specify layoutName. And you can dynamically compute the layout name based on a parameter passed into the component.
App.XRenderComponent = Ember.Component.extend({
tagName: "",
layoutName: function(){
return this.get('displayLayout.type');
}.property('displayLayout'),
});
Then you can just do
<script type="text/x-handlebars" id="index">
{{#each layout in model}}
{{ x-render displayLayout=layout }}
{{/each}}
</script>
See working example here

Related

Using variable in partial name

I have a list of "summaries" in a loop:
{{#each formSummaries}}
{{/each}}
For each one I want to output a template that is named by each formSummary.name:
{{#each formSummaries}}
{{partial 'forms/summaries/' + name}}
{{/each}}
or like this:
Controller:
summaryPath: 'forms/summaries' + name
Template:
{{#each formSummaries}}
{{partial summaryPath}}
{{/each}}
Is it possible to do something like this with a partial? Is there a more "Ember" way of solving this problem? Thank you in advance.
The handlebars looks good to me, except that I would specify an itemController. This will let you get fancy-pants with each rendered model through the use of computed properties and other powerful controller mechanisms.
{{#each formSummaries itemController='summary'}}
{{partial summaryPath}}
{{/each}}
Now, summaryPath should be computed within that item controller. How about:
App.SummaryController = Ember.ObjectController.extend({
summaryPath: function () {
return 'templateName_' + this.get('name'); // You can tailor this part to suit your needs.
}.property('name')
)};
Hope that helps!

Using Ember, how do I dynamically bind a property in a view helper contained in an each block?

I have a template with code similar to:
{{#each item in items}}
<label>{{item.name}}</label>
{{view Ember.Select content=selectableValues value={{item.name}}}}
{{/each}}
I understand the code above won't work, but it illustrates my problem and what I want to achieve. I don't want to bind what's selected in the Ember.Select to the property of the iterated item, but rather to a controller property of the name of the iterated item's name property.
How might I do this?
You're going to have to dynamically create a computed property, since you are binding the value of the value, unfortunately, this presents problems if you then decide to change the value of the value, but, I'll leave that up to you to play with.
<script type="text/x-handlebars" data-template-name="index">
{{input value=foo}}
{{view App.BlahView host=controller propName=chacha}}
</script>
<script type="text/x-handlebars" data-template-name="blah">
{{view.propValue}}
</script>
Code
App.IndexController = Em.Controller.extend({
chacha:'foo',
foo:'bar'
});
App.BlahView = Em.View.extend({
templateName:'blah',
setupDyna: function(){
var propName = this.get('propName');
Ember.defineProperty(this, 'propValue', Ember.computed(function() {
return this.get('host').get(propName);
}).property("host." + propName));
}.on('init')
});
http://emberjs.jsbin.com/vunureno/1/edit

Ember.js: ArrayController undefined in template

Problem:
I am kind of struggling with the organization of my first ember app. The current issue is that the my Items ArrayController is not defined in my dashboard template:
<script type="text/x-handlebars" data-template-name="dashboard">
{{#if controllers.items}}
<p class="alert alert-error">Dashboard can access item's info - Nice!</p>
{{else}}
<p class="alert alert-error">Dashboard cannot access items... :-/</p>
{{/if}}
</script>
Likely cause: *
**EDIT: after talking with #conrad below, I'm kind of questioning this:*
I had a similar issue in an earlier post and kingpin2k suggested the cause was that I:
"never created anything that uses the options controller".
This is probably the case here as well. This quick screencast shows that a breakpoint on my ArrayController is not hit on page load - but it is hit when I inspect the Items controller in the Ember inspector tool (eg, Ember creates the ArrayController object right then for the first time).
Apparent non-solutions:
My Dashboard controller says it needs the Items controller. I guess that isn't enough to instantiate the ArrayController?
App.ItemsController = Ember.ArrayController.extend({
len: function(){
return this.get('length');
}.property('length'),
totalCost: function() {
return this.reduce( function(prevCost, item){
return parseInt(item.get('values').findBy('type', 'cost').price, 10) + prevCost;
}, 0);
}.property('#each.values')
[more computed properties...]
});
App.DashboardController = Em.Controller.extend({
needs: ['items'],
itemsLength: Ember.computed.alias('controllers.items.len'),
itemsTotalCost: Ember.computed.alias('controllers.items.totalCost'),
[more computed properties...]
});
Furthermore, each item in Items is being rendered in my items template. I guess that does not create the missing controllers.items either...
<script type="text/x-handlebars" data-template-name="items">
{{#each}}
[these render fine]
{{/each}}
</script>
<script type="text/x-handlebars" data-template-name="display">
<!-- DISPLAY TEMPLATE -->
{{!- DASHBOARD -}}
{{render dashboard}}
{{!- ITEMS -}}
{{render 'items' items}}
</script>
So then.. what?
I can imagine many possible avenues, but haven't gotten any of them to work yet:
Specify the Items ArrayController in {{render dashboard}}?
Some configuration in a Route?
Maybe my templates/routes are not correctly arranged?
You could make sure that the ItemController is instantiated in the dashboard template by calling it in the DashboardController's init function:
App.DashboardController = Em.Controller.extend({
needs: ['items'],
init: function() {
this._super();
this.get('controllers.items.length');
}
});
/edit:
removed the part that was not helpful

Calling a handlebars block helper from another helper

How can I call a handlebars block helper from another helper in ember.
I am interested in converting the following in a bound helper.
{{#each link in sideMenuLinks}}
<li class="navigation page">
{{#linkTo ROUTE_VARIABLE link.linkToRouteContext}}
{{{link.iconTag}}}<i class="icon-right-open"></i>{{link.linkText}}</a>
{{/linkTo}}
</li>
{{/each}}
Note that I will have to call a {{#linkTo}} block inside the helper and also change ROUTE_VARIABLE with a value from the property link.linkToRoute.
The Ember handlebars helpers are placed at Ember.Handlebars.helpers. You can call them with Ember.Handlebars.helpers.{helperName}.call.
However, The above looks like a dynamic partial/view style helper. I would suggest creating a Handlebars View helper for it. The syntax is similar but you pass in a View class to the helper.
Ember.Handlebars.helper('sideMenuLinks', App.SideMenuLinksView);
The corresponding view can use a template similar to yours by giving a templateName
App.SideMenuLinksView = Ember.View.extend({
templateName: 'sideMenuLinksTemplate'
});
You template would be something like,
<script type='text/x-handlebars' data-template-name='sideMenuLinksTemplate'>
{{#each link in view.links}}
<li class="navigation page">
{{#linkFor parentView.routeVariable link.linkToRouteContext}}
{{{link.iconTag}}}<i class="icon-right-open"></i>{{link.linkText}}</a>
{{/linkFor}}
</li>
{{/each}}
</script>
The default Ember linkTo is static, In that you cannot pass the named route from a variable. You will need a dynamic linkTo helper like the linkFor that looks up the variable path before
applying linkTo internally.
Ember.Handlebars.registerHelper('linkFor', function(path, params, options) {
var view = options.data.view;
var name = view.get(path);
var args = [name, params, options];
return Ember.Handlebars.helpers.linkTo.apply(this, args);
});
Finally you can use this helper like below. The links property will be bound to the content of the controller in this example.
<script type='text/x-handlebars' data-template-name='application'>
{{sideMenuLinks links=content routeVariable='page'}}
</script>
Here's a working jsbin.

Escape keyword in the handlebar template

I have a collection of objects (MyApp.instrsController, an ArrayController) and each object has a property called 'action' (a float number), how can I display it using the handlebars template?
Below is the code I've tried:
<ul>
{{#each MyApp.instrsController}}
<li>{{action}}</li>
{{/each}}
</ul>
But since 'action' is a 'keyword', it throws javascript runtime error 'options is undefined'.
You can manually specify that you're looking for a property in the current context by preceding the property name with this.:
{{this.action}}
If you can't change the property name you can use a computed property in your model object, see http://jsfiddle.net/pangratz666/4ZQM8/:
Handlebars:
<script type="text/x-handlebars" >
<ul>
{{#each App.controller}}
<li>{{actionProp}}</li>
{{/each}}
</ul>
</script>
JavaScript:
App.Object = Ember.Object.extend({
actionProp: function() {
return this.get('action');
}.property('action')
});
App.controller = Ember.ArrayController.create({
content: [],
addObj: function(number) {
this.pushObject(App.Object.create({
action: number
}));
}
});
If you don't have a custom model object, you can use a computed property on a CollectionView, see http://jsfiddle.net/pangratz666/r6XAc/:
Handlebars:
<script type="text/x-handlebars" data-template-name="item" >
{{actionProp}}
</script>​
JavaScript:
Ember.CollectionView.create({
contentBinding: 'App.controller',
itemViewClass: Ember.View.extend({
templateName: 'item',
actionProp: function(){
return this.getPath('content.action');
}.property()
})
}).append();
I seriously suggest that you just change the name of the property.
You are spending time working on a problem that is not core to your domain.
YAGNI. KISS.
The biggest lesson to learn in programming is how to get things made and done rather than how to manipulate javascript into not throwing a snit over your use of a reserved keyword. You will be happier later if you don't have a fragile solution that looks tricky to understanc
Since this is a float, can I suggest you use "actionLevel"?
just use model.property,such as:
{{input class="input-xlarge" value=model.content }}