Expandable table row with EmberJS view - ember.js

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?

Related

How can I load a model after using {{link-to}} with a model passed in already

In Ember 2.5.
{{#each model.vehicletypes as |vt|}}
<tr>
...
<td class="text-center text-success">{{#link-to "admin.vehicletypes.edit" vt}}<i class="material-icons">create</i>{{/link-to}}</td>
<td class="text-center text-danger"><i class="material-icons" {{action "delete" vt}}>delete</i></td>
</tr>
{{/each}}
When clicking the {{link-to}} to edit a vehicle type, it passes model.vehicletypes. Once I am on the edit page, I need to get another model to populate a list. But navigating with a model passed in already means that no other model will be requested.
How can I request another model in the edit page?
If you pass an object, the model hook won't fire. Instead, pass vt.id and use that id to make the requests in the edit.js route.

{{#link-to}} helper not working with supplied model

I have the following index template:
<table class="table table-striped table-condensed table-hover">
<thead>
<tr>
<th>name</th>
</tr>
</thead>
<tbody>
{{#each agent in model}}
<tr>
<td>{{#link-to "agents.show" agent}}{{agent.name}}{{/link-to}}</td>
</tr>
{{/each}}
</tbody>
</table>
I have defined a link-to to a show route, but this is not working: it is creating the following link:
http://localhost:9001/#/nodes/agents/undefined/show
With an undefined dynamic segment. If instead I directly supply the .id of the agent:
<td>{{#link-to "agents.show" agent.id}}{{agent.name}}{{/link-to}}</td>
The link is created fine. But I do not want to do this because that means a round-trip to the server, while I already have the model available in the index controller. Besides, the link-to helper has always worked fine with a supplied model (I have refactored my app quite a bit, and I am unable to find the source of this problem)
Why is link-to able to use the object id for the dynamic segment, but it is not able to pass the full object to the linked route? What can I do to further debug this issue?

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>

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