sris
sris

Reputation: 4978

How to create a multiselect list in Ember.js

I'm trying to figure out how to build a small app consisting of a list where you can select multiple items and toggle to select all/none and see the number of currently selected rows.

I believe that the "selected" state should not be part of the model objects, but I cannot really figure out how to do it.

This is my current setup (which doesn't work obviously yet)

Runnable code http://jsfiddle.net/jacobk/rU35G/1/

var App = Ember.Application.create();

App.ApplicationRoute = Ember.Route.extend({
    model: function() { return Ember.A(["Foo", "Bar", "Baz"]); }
});

App.ApplicationController = Ember.ArrayController.extend({
    allSelected: false,

    selectedCount: function() {
        return 0;
    }.property()
});

App.RowController = Ember.ObjectController.extend({
    isSelected: false
});
<script type="text/x-handlebars" data-template-name="application">
    <h3>{{ selectedCount }} rows selected.</h3>
    <label>
    {{view Ember.Checkbox checkedBinding="allSelected"}}
        Toggle select all
    </label>
    <hr/>
    <ul>
    {{#each controller itemController="row"}}
        <li {{bindAttr class="isSelected"}}>
            {{view Ember.Checkbox checkedBinding="isSelected"}} {{this.content}}
        </li>
    {{/each}}
    </ul>
</script>

I'm trying to understand when to use bindings, observers, properties, "needs" etc. and when its appropriate to use controllers vs views and so on. I've yet to grok the general flow of information/data in ember apps.

e.g. should the ArrayController from my example above iterate over the "contained" views/controllers and change the "selected" state when the "select all" check box is toggled OR should all the "sub controllers" observe/"have bindings to" the ArrayController and change themselves when it changes, and if so, how should I propagate data the opposite direction. How would the ArrayController get "all currently selected" rows?

I would love to see the "canonical solution" for this.

Upvotes: 4

Views: 1981

Answers (2)

Aaron Renoir
Aaron Renoir

Reputation: 4391

I agree about keeping the selected state out of the model. You need to define the itemController in the Ember.ArrayController.

here is a working example. http://jsbin.com/sunat/3/edit

App.RowController = Ember.ObjectController.extend({
  isSelected: false
});

App.IndexController = Ember.ArrayController.extend({
  itemController: 'row',

  selectAll: function(key, value) {

    if (arguments.length == 2) {
      this.setEach('isSelected', value);

      return value;
    } else {

      return this.isEvery('isSelected', true);
    }
  }.property('@each.isSelected')
});

#template
<script type="text/x-handlebars" id="index" >
  <label>
    {{input type="checkbox" checked=selectAll}}
    Toggle select all
  </label>
  <hr/>
  <ul>
  {{#each}}
    <li>
      {{input type="checkbox" checked=isSelected}} {{name}}
    </li>
  {{/each}}
  </ul>
</script>

Upvotes: 0

Dinesh
Dinesh

Reputation: 111

No need of row controller. @each, computed property and checkedbinding can be utilized to solve this as shown below. isSelected has to be defined in content of the arraycontroller:

    App.ApplicationController = Ember.ArrayController.extend({

        allSelected: function(key, value) {
    if ( value !== undefined ) {
     // when check box is ticked, this gets executed
      this.setEach( 'isSelected', value );
      return value;
    } else {
        //as a computed property
      return !!this.get( 'length' ) &&
        this.everyProperty( 'isSelected', true );
    }
  }.property('@each.isSelected')

        selectedCount: function() {
            return 0;
        }.property()
    });

Upvotes: 1

Related Questions