If the user clicks the li
entry, it triggers the useThisAddress
action, which sets isSelected
in the address
object to true
. From there bind-attr
takes care of setting the appropriate class (ie `border-black').
Is this the right way to go about this? Or is there a better approach?
Below code works. However, I am a bit uncertain about setting isSelected
to true
on the address
object, since it isn't a real property in the address
object model.
// Controller
import Ember from 'ember';
export default Ember.Controller.extend({
active: null,
actions: {
useThisAddress: function(address) {
address.set('isSelected', true);
var active = this.get('active');
if (active) {
active.set('isSelected', false);
}
this.set('active', address);
}
}
})
// Template
<ul>
{{#each address in model}}
<li {{bind-attr class='address.isSelected:border-black'}} {{action 'useThisAddress' address}}>
Address: {{address.address1}} {{address.address2}}, {{address.city}}, {{address.postalCode}}
</li>
{{/each}}
</ul>
I think your gut reaction is correct - you shouldn't be setting properties on your models that are strictly related to your UI.
Components are the building blocks in Ember for storing UI-layer state. It's a somewhat tricky time in Ember, because Controllers are also used for this purpose, but they're on the way out. Soon it will be all components.
When I encounter a case like yours (list-with-selected-items), I usually make a list
component and a list-item
component. The list
knows which item is selected, and each list-item
knows whether its selected. For your simple case one of the other solutions may be more straight-forward, but I find typically with these lists, you'll eventually want more functionality (bind to selected item, more complex templates, etc.).
So one approach may look like this:
{{#address-list selected=selectedAddress as |list|}}
{{#each address in model}}
{{#address-list-item item=address list=list}}
<p>{{address.address1}}</p>
{{/address-list-item}}
{{/each}}
{{/address-list}}
You use block params (note: Ember 1.10 or greater required) to pass the <address-list>
into each <address-list-item>
. Now, your address list item can tell the list a new address has been selected, by sending an action to it:
App.AddressListItemComponent = Ember.Component.extend({
classNameBindings: ['active'],
active: function() {
return this.get('list.selected') === this.get('item');
}.property('list.selected', 'item'),
click: function() {
this.get('list').send('select', this.get('item'));
}
});
App.AddressListComponent = Ember.Component.extend({
actions: {
select: function(item) {
this.set('selected', item);
}
}
});
This sets up all the bindings for you, adds the .active
class to each <address-list-item>
, and lets you bind to the <address-list>
's selected item from outside the template.
Now, targeting the list
like this to use .send
is awkward, which is exactly why soon we'll be able to pass actions down into child components (check out the Road to Ember 2.0 guide and search for "Improving Actions"). Once that functionality lands, you'll be able to pass the select
action handler from the <address-list>
directly into each <address-list-item>
. But this is one way to go, for now, and it'll make refactoring pretty straightforward when those changes do land.