Table - multi-row select - selected property on controller


#1

I have an overview table that displays my model. I need to be able to select multiple records from that table to perform an action on that collection of selected items. So I’ve added a checkbox on each row.

Do I need to bind that checkbox onto a model property (introducing an isSelected property on my model), or can it be done on the controller level ?

I know the TodoMvc app does the binding to the model that, but there the isCompleted is really part of the core “todo” model.

In my case, it’s a pure UI thing (toggling a selection).

I’ve been playing around with this and I can get it to work but only if I bind it to my model.

The overview table :

{{#each item in controller itemController="location"}}
    <tr>
    <td>{{view Ember.Checkbox checkedBinding="selected"}}</td>
    <td>{{item.prop1}}</td>
    <td>{{item.prop2}}</td>
    </tr>
{{/each}}

counter = {{editCounter}}
<button {{action 'handleSelectedRecords'}}>Handle selected</button>

The selected binding is done on the location controller (ObjectController) and not directly on the model. The editCounter and handleSelectedRecords are defined on the ArrayController that displays the table

The selected property on the array controller looks like this:

selected: function(key,value) {
	//this.get("content").set("selected",value);  // if I don't do this ten the editCounter is never triggered.
	return value;
}.property()

Notice how the rest of the solution fails if I don’t update the selected property on the model.

The counter is implemented like this:

editCounter: function () {
	return this.filterProperty('selected', true).get('length');  // again this works on the model property I guess
}.property('@each.selected')

And the handleSelectedRecords like this

handleSelectedRecords: function() { arr = this.filterProperty(‘selected’, true); for (i=0 ; i<arr.length ; i++) { console.log("found selected " + arr[i]) } }

Is there a way to do this without putting the “selected” property on them model ? From a design point of view, the selected property should be part of the controller, but then how do I get the editCounter and the handleSelectedRecords to work ?


#2

You can achieve this by defining an item controller on your array controller. Like regular controllers, the item controller will wrap each of your model objects and proxy them, allowing you to define extra behaviour / properties on them without polluting your model.

There’s a description of this in the api docs http://emberjs.com/api/classes/Ember.ArrayController.html


#3

I did specify an itemController on the ArrayController. The idea was to expose the selected property on that itemController. And that’s what I did except it was not working as I hoped.

The issue being that I need to call this for the whole thing to work.

this.get("content").set("selected",value);

I’ve added a jsfiddle. It would be great if you could give a pointer on how to get the fiddle working without having that selected property on the model.

http://jsfiddle.net/ddewaele/VBZA8/


#4

Think I got it working …

http://jsfiddle.net/ddewaele/RqUPu/

I removed the entire selected property from the ObjectController and removed it from the model and it worked.

So I guess the following statement causes a selected property to be made available on the controller and that’s all I need.

{{view Ember.Checkbox checkedBinding="selected"}}

So I guess this would be correct way to go about it ?


#5

Another thing .

Does the fact that I already defined an itemController here mean that I don’t need to put it on my ArrayController anymore ?

{{#each item in controller itemController="location"}}
    <tr>
    <td>selected for {{item.id}} = {{selected}}</td>
    <td>{{view Ember.Checkbox checkedBinding="selected"}}</td>
    <td><input type="checkbox" {{action 'find'}} class="toggle"></td>
    <td>{{item.latitude}}</td>
    <td>{{item.longitude}}</td>
    <td><button {{action 'find'}}>Find something</button></td>
    </tr>
{{/each}}

So for my understanding:

  • the checkbox will bind the “selected” property on the itemController=location.
  • computed properties that depend on @each.selected continue to work as we now have a controller (the itemController) that deals with the selected property.
  • filterProperties like filterProperty('selected', true) continue to work as we now have a controller (the itemController) that deals with the selected property.

Would that be correct ?


#6

Sounds correct to me. I’m using a similar approach and made this mixin to add selection capabilities to any controller:

App.SelectableMixin = Ember.Mixin.create({
  selectedItems: function(key, items) {
    if(arguments.length === 2) { //setter
      var ids = items.mapProperty('id');
      Ember.changeProperties(function() {
        this.forEach(function(item) {
          item.set('isSelected', ids.contains(item.get('id')));
        });
      }, this);
    }
    return this.filterProperty('isSelected', true);
  }.property('@each.isSelected'),

  selectedAll: function(key, selectAll) {
    if(arguments.length === 1) { //getter
      return this.get('length') > 0 && this.everyProperty('isSelected', true);
    } else { //setter
      Ember.changeProperties(function() {
        this.setEach('isSelected', selectAll);
      }, this);
      return selectAll;
    }
  }.property('@each.isSelected'),

  selectedNone: function() {
    return this.get('selectedItems.length') === 0;
  }.property('selectedItems.length').readOnly()
});

There’s also a more complete solution in the Emberella library here: https://github.com/realityendshere/emberella/blob/master/packages/emberella/lib/mixins/selectable_mixin.coffee


#7

My apologies, I missed the item controller defined in the template of your original post. I’m glad you got it working. I wouldn’t have thought that it would work without defining a property on the item controller but it looks like it’s ok.

Looking at your original example, I think the problem is that you were defining that property as a computed property that referred to the model. This would have worked if you just defined it as a regular property without any reference to the model at all. I suspect that this is what is going on now without any property being defined. The selected value is probably being stored on the item controller.