An Ember Multi-select Checkbox
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.
// 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.