Last week, I needed to create a series of multi-select checkboxes for a project that I’m working on. The client side framework is EmberJS. There were a few open-source options out there. Unfortunately, they use the existing Ember-CLI. Our project, being a bit older and out of date, cannot use Ember CLI.

But, rolling your own isn’t so difficult. Let’s start with what we’re trying to accomplish. We want to turn a list of checked items into an array of strings.

Multi-select checkboxes

// Our model has a property called "permissions". This property is an
// array of strings.
App.IndexRoute = Ember.Router.extend({
  model: function () {
    return {
      /* snip */
      permissions: []
      /* snip */
    };
  }
});

// Our controller has an array of available options. As usual, the `label`
// is displayed on screen. The `value` is the string that is stored on the
// model.
App.IndexController = Ember.Controller.extend({
  adminOptions: [{
    label: 'Can create users?',
    value: 'create_users'
  }, {
    label: 'Can disable users?',
    value: 'disable_users'
  }, {
    label: 'Can edit users?',
    value: 'edit_users'
  }],
  // This is used to display the selected permissions in the UI. It is
  // not required as part of the solution.
  selectedAsString: Ember.computed('model.permissions.[]', function () {
    return JSON.stringify(this.get('model.permissions'));
  })
});

Our component is written in two parts. The first part is the checkbox element itself. The second is the actual component.

// Each available option becomes an instance of a "MultiSelectCheckbox" object.
var MultiSelectCheckbox = Ember.Object.extend({
  label: 'label',
  value: 'value',
  isChecked: false,
  changeValue: function () { },
  onIsCheckedChanged: Ember.observer('isChecked', function () {
    var fn = (this.get('isChecked') === true) ? 'pushObject' : 'removeObject';
    this.get('changeValue').call(this, fn, this.get('value'));
  })
});

App.MultiSelectCheckboxesComponent = Ember.Component.extend({
  labelProperty: 'label',
  valueProperty: 'value',
  // The list of available options.
  options: [],
  // The collection of selected options. This should be a property on
  // a model. It should be a simple array of strings.
  selected: [],
  checkboxes: Ember.computed('options', function () {
    var _this = this;
    var labelProperty = this.get('labelProperty');
    var valueProperty = this.get('valueProperty');
    var selected = this.get('selected');
    return this.get('options').map(function (opt) {
      var label = opt[labelProperty];
      var value = opt[valueProperty];
      var isChecked = selected.contains(value);
      return MultiSelectCheckbox.create({
        label: label,
        value: value,
        isChecked: isChecked,
        changeValue: function (fn, value) {
          _this.get('selected')[fn](value);
        }
      });
    });
  })
});

Here is our (very simple) component template.

{{#each checkboxes as |checkbox|}}
  <p>
    <label>
      {{input type='checkbox' checked=checkbox.isChecked}}
      {{checkbox.label}}
    </label>
  </p>
{{/each}}

Finally, to make use of our component, write the following in your template.

{{multi-select-checkboxes options=adminOptions selected=model.permissions}}

That’s really all it takes. A fully working example of this code is available at JSBin.