Accessing Values for Key in Handlebars & Ember - ember.js

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

Related

Expandable table row with EmberJS view

I'm new to Ember and trying to learn by following tutorials and documentation I can find on the Net.
I'm trying to wrap my head around custom Ember views and when to use them.
I have test case where I list a number of products in a table. Once you click on a specific product, I would like for a hidden row or div to display right below the currently selected product and potentially make an Ajax call to a web service to retrieve more information about the product. Inside this open div or row I might need to edit the fields etc. Should I be using an Ember view for this? When do you trigger click events for the row etc? In which controller would you handle all of the necessary events and extra product information being returned from the service etc.
I've created a JS fiddle with the basic setup, but none of the click events. Has anyone done something similar that I could have a look at, or would be able to assist?
http://jsfiddle.net/jc79aoap/1/
This is just the basic table structure that lists the products, showing the rows that I would like to be clickable:
<table class="table table-bordered">
<thead>
<tr>
<th>ID</th>
<th>Title</th>
<th>Description</th>
</tr>
</thead>
<tbody>
{{#each}}
<tr>
<td>{{id}}</td>
<td>{{title}}</td>
<td>{{description}}</td>
</tr>
{{/each}}
</tbody>
</table>
Thanks
Conception
This is a structure problem that I have faced some time ago. I have developed following approach. Let's start with defining some necessary controllers
App.ProductsController = Ember.ArrayController.extend({
itemController: 'product'
});
App.ProductController = Ember.ObjectController.extend({
displayDetails: false,
actions: {
toggleDetails: function() {
this.toggleProperty('displayDetails');
return;
}
}
});
and corresponding view
{{#each}}
<tr {{action 'toggleDetails'}}>
<td>{{id}}</td>
<td>{{title}}</td>
<td>{{description}}</td>
</tr>
{{#if displayDetails}}
<tr>
<td colspan="3">details here to {{id}}</td>
</tr>
{{/if}}
{{/each}}
You can of course move logic inside condition to another controller
{{#if displayDetails}}
{{render 'productDetails'}}
{{/if}}
You should get desired effect as you can see there
Pros and cons
You are won't have the state of URL changed after click and you won't get ProductRoute.
You can use still load data asynchronously by using ember data with async relationship between models. I am quite not sure whether is it good to do things this way in MVC framework. With time it's going to be a huge mess, believe me.
Conclusion
I don't know whether is there better approach at the moment but you should think a little bit about your UX schema. Maybe it would be better to render sub results in the sidebar or redirect user to different page?

Get property by variable name

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.

View-targeted action propagating to sibling views

I'm trying to implement a basic table, with two rows for each player object. One is the edit row, filled with text fields, the other is the view row.
In my main template I'm using a CollectionView to render out the set of rows (players is a collection)
<table>
{{view Ember.CollectionView contentBinding="players" itemViewClass="App.PlayerView"}}
</table>
I then defined a custom view to receive the click action:
App.PlayerView = Ember.View.extend({
templateName: 'rosters/player_view',
isShowVisible: true,
actions: {
toggleVisibility: function(){
this.toggleProperty('isShowVisible');
}
}
});
Finally, I created the template for the view:
{{#if view.isShowVisible}}
<tr>
<td>{{view.content.name}}</td>
<td><a {{action "toggleVisibility" target="view"}}>Edit</a></td>
</tr>
{{else}}
<tr>
<td>{{input type="text" value=view.content.name}}</td>
<td><a {{action "toggleVisibility" target="view"}}>Done</a></td>
{{/if}}
What I'd like to happen is that when I press the "Edit" link, it hides the text row, and shows the input row. What's happening is that it works fine if I press the first link, but, say I press the 10th link down, it will hide the show rows of 1-10, and then only show the edit of 10. It's almost like the event is propagating to the sibling views from the collection, or their attributes are somehow linked?
I tried setting "bubble=false" on the action, but that didn't solve anything, neither did returning false from my action, or preventing propagation inside there. An alert statement inside the action indicates that it's only being called once (so, not once for each sibling view). I'm new to Ember, so I'm open to any suggestions over what I'm doing wrong!
It's actually because of missing closing tr tag in the else statement. Although this is ultimately the issue, I think when metamorph rips out and adds the table rows the browser has issues, but that can easily be remedied by pulling the tr tags out of the if statement.
http://emberjs.jsbin.com/EXEnUZE/1/edit
<script type="text/x-handlebars" data-template-name="rosters/player_view">
<tr>
{{#if view.isShowVisible}}
<td>{{view.content.color}}</td>
<td><a {{action "toggleVisibility" target="view"}}>Edit</a></td>
{{else}}
<td>{{input type="text" value=view.content.color}}</td>
<td><a {{action "toggleVisibility" target="view"}}>Done</a></td>
{{/if}}
</tr>
</script>

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
}
});

ember.js contentIndex for each helper

I have the following handlebars template:
{{#each Lead.leads_controller}}
<tr>
<td>
<strong>{{???}}</strong>
</td>
<td class="plus expander"> </td>
<td>{{company}}</td>
<td>{{web}}/</td>
<td>{{phone_1}}</td>
<td>{{phone_2}}</td>
<td>{{fax}}</td>
</tr>
{{/each}}
I want to show a 1 based index for each row in a table. I know previously the collectionView had contentIndex exposed but this does not appear to be the case for the each helper.
I found this that seems to suggest that this is not available for the each helper. Is there any way of making this possible for the each helper?