Get property by variable name - ember.js

I want to render a table using emberjs templates. The table should resolve the columns to be rendered dynamically:
<table>
{{#each item in this.items}}
<tr>
{{#each colName in this.columnNames}}
<td>{{item.[colName]}}</td>
{{/each}}
</tr>
{{/each}}
</table>
However, I think handlebars tries to access the property colName of an item. How can I access the property dynamically?

UPDATE: As Visualize pointed out in the comments, there is now a native get helper. You should prefer that over the implementation below (which may not update properly with newer versions of Ember.)
I'm not sure if Handlebars is able to dynamically resolve a variable like that (at least out of the box). I would probably write my own helper.
Ember.Handlebars.helper('getProperty', function(object, property, options) {
return Ember.get(object, property);
});
Then in your template:
{{#each item in items}}
<tr>
{{#each colName in columnNames}}
<td>{{getProperty item colName}}</td>
{{/each}}
</tr>
{{/each}}
And if my understanding of Ember bindings is correct, the helper should re-render any time item or colName is changed.

{{#each item in items}}
<tr>
{{#each colName in columnNames}}
<td>{{get item colName}}</td>
{{/each}}
</tr>
{{/each}}
Using get helper.
Thank you.

Related

Binding to model in template based on a list of strings

In my controller I have a list of strings like this:
columns : ['name', 'description']
I want to filter what is displayed in the template based on this list:
{{#each m in controller.model}}
{{#each col in controller.columns}}
<td>{{m[col]}}</td>
{{/each}}
{{/each}}
Of course {{m[col]}} is not valid code but you get the idea. How might I achieve this?
This code is generic so I have no way of telling what the columns array contains before hand.
You can create a bound helper as follows:
Ember.Handlebars.registerBoundHelper('dd', function(rowData, col) {
return rowData[col];
});
Then, you can use it in your template as follows:
{{#each item in model}}
<tr>
{{#each col in columns}}
<td> {{ dd item col }} </td>
{{/each}}
</tr>
{{/each}}
Working example on jsbin here
If you want the data bound to the template so that it updates when the data changes, use a small component as follows:
App.getPropertyComponent = Ember.Component.extend({
tagName: '',
value: function() {
var model = this.get('model');
var method = this.get('method');
return model[method];
}.property('model.{name,description}')
});
Notice that you have to manually specify each property you are binding to.
Component template:
{{value}}
Then, you can use it in your template as follows:
{{#each item in model}}
<tr>
{{#each col in columns}}
<td>{{get-property model=item method=col}}</td>
{{/each}}
</tr>
{{/each}}

Nested each and if with Handlebars and EmberJS

I have two list of models which have relations.
A has many B
I loop through B with {{#each}} and generate a <table> with it and every row has a <select> where all As, that exist, are listed.
If an B belongs to an A it should be selected in the <select>
Example:
<table>
{{#each b in listofb}}
<tr><td>
<select>
{{#each a in listofa}}
{{#if b belongsto a}}
<option selected>a.someAttribute</option>
{{else}}
<option>a.someAttribute</option>
{{/if}}
{{/each}}
</select>
</td></tr>
{{/each}}
</table>
There is probably a way to do this with handlebar-helpers, but I don't know if I can use their output later with Ember controller actions.
Use Ember.Select http://emberjs.com/api/classes/Ember.Select.html
{{view 'select' value=someControllerProperty}}
It helps you keep logic inside the controller.
Now, in the controller you can react upon changes to the value selected with:
processValueChange: function() {
// do stuff
}.property('someControllerProperty')

Accessing Values for Key in Handlebars & Ember

Is it possible to access properties in a value-for-key style in Handlebars?
I have a CollectionView which uses an ArrayController full of models. The CollectionView has a property called 'columns' that define table column configurations for rendering.
Ideally I'd be able to loop through each column (see example below) ensuring that only the columns we want rendered are rendered (and later, formatting and other attributes are applied)
<tr>
{{#each column in view.controller.columns}}
<td>
{{ view.content.[column.name] }}
</td>
{{/each}}
</tr>
This doesn't work, it just returns no content.
I've also tried these other styles to see if they'd work:
<tr>
{{#each column in view.controller.columns}}
<td>
{{ view.content.name }}
{{ view.content.[column.name] }}
{{valForKey view.content column.name }}
</td>
{{/each}}
</tr>
The valForKey helper is one I wrote (source here), which does display the correct value but doesn't bind, so the value isn't updated when the property changes.
What's the best way to handle this use case in Ember?
Thanks
Right now Ember has get helper included:
{{get object key}}
You can create a bound helper to display the value of the column
Ember.Handlebars.registerBoundHelper('dd', function(rowData, col) {
return rowData[col];
});
See the following SO answer for more details
https://stackoverflow.com/a/27477602/908842

How do I establish associations between multiple ember views and their associated objects?

I'm confused about the relationship between templates, views and models.
I understand that one of the primary roles of a view is to translate primitive events into semantic events, but I'm unclear how I should provide the necessary data to a view.
I've got a calendar_month template. The associated CalendarMonth object has an array of CalendarWeek objects, each of which has an array of CalendarDay objects. (These are not models, since they don't persist -- they're objects, generated in the CalendarMonthRoute).
So my template looks like this:
<table>
{{#each week in weeks}}
<tr>
{{#each day in week.days}}
<td>
{{#view App.CalendarDayView}}
{{ day.monthDay }}
{{/view}}
</td>
{{/each}}
</tr>
{{/each}}
</table>
I'd like each of the CalendarDayView to respond to clicks, but I don't know how to get access to the specific calendar day object, which should be associated with each corresponding CalendarDayView.
App.CalendarDayView = Ember.View.extend({
click: function(evt) {
// do something here... but how do I know which day was clicked?
}
});
I assume it's likely I'm not setting this up properly. Should each CalendarDay view have it's own associated instance of a CalendarDayController, providing proxy access to the corresponding CalendarDay object? If so, wouldn't that then require multiple instances of the CalendarDayController? (My understanding is that the controllers are singletons -- only one of each type should exist.)
Any advice much appreciated.
This can be easily achieved by using an action helper, with which you can easily pass context objects to handler methods:
<table>
{{#each week in weeks}}
<tr>
{{#each day in week.days}}
<td>
{{#view App.CalendarDayView}}
<a {{action yourAction day target="view"> {{ day.monthDay }} </a>
{{/view}}
</td>
{{/each}}
</tr>
{{/each}}
</table>
And the corresponding View:
Vollipop.CalendarDayView = Ember.View.extend({
yourAction : function(day){
// perform your work on the day
}
});

Dynamic class binding on a nested child view in #each - how?

I had a single view with an each helper similar to this:
<table class="select-sect" cellspacing="0">
{{#each sections}}
<tr {{bindAttr class="highlight:highlight"}} {{action "selectSection" }}>
<td class="key">
{{#each zones}}
<em {{bindAttr style="color"}}> </em>
{{/each}}
</td>
<td class="sect">{{name}}</td>
<td class="price">{{currency lowPrice}} - {{currency highPrice}}</td>
</tr>
{{/each}}
</table>
Binding a dynamic class like this worked very well. If I set section.highlight == true in a controller, the view would update with the appropriate class.
"Calling" code:
zone.section.set('highlight', true);
Because I need to handle some other events on each row, I've migrated the entire table row to a nested view. I'm searching for a way to make the dynamic class work as it used to.
{{#each sections}}
{{#view SYOS.SelectSectionRowView sectionBinding="this" }}
<td class="key">
{{#each section.zones}}
<em {{bindAttr style="color"}}> </em>
{{/each}}
</td>
<td class="sect">{{section.name}}</td>
<td class="price">{{currency section.lowPrice}} - {{currency section.highPrice}}</td>
{{/view}}
{{/each}}
I don't think I can use the same bindAttr solution since it would need to apply to the #view helper. I've also tried classNameBindings & classBinding to no avail. Updating section.highlight no longer triggers this view to apply the dynamic class.
View w/ classNameBindings:
SYOS.SelectSectionRowView = Em.View.extend({
tagName: 'tr',
classNameBindings: ['isHighlighted:highlight'],
isHighlighted: function () {
return this.section.highlight;
} //also tried .property('section')
});
View with classBinding:
{{#view SYOS.SelectSectionRowView sectionBinding="this" classBinding="needsHighlight"}}
in view class:
needsHighlight: function () {
if (this.section.highlight) {
return 'highlight';
}
return '';
} .property('section'),
Neither of these seems to do the trick. Can anyone lend any insight into how to get a scenario like this going?
Thanks much!
try classNameBindings: ['section.highlight:highlight']