Martin
Martin

Reputation: 2300

Ember.js hasMany as list of checkboxes

I have the following two models:

App.Child = DS.Model.extend({
    name: DS.attr('string')
});

And:

App.Activity = DS.Model.extend({
    children: DS.hasMany('child',{async:true}),
    name: DS.attr('string')
});

I want to use checkboxes to choose between the existing children, for the hasMany relation.

For example, I have these three children:

App.Child.FIXTURES = [
  { id: 1, name: 'Brian' },
  { id: 2, name: 'Michael' },
  { id: 3, name: 'James' }
];

The user should be able to use checkboxes, while creating or editing an activity, for choosing which children, to add to the hasMany relation.

I've created a JSFiddle to illustrate my question: http://jsfiddle.net/Dd6Wh/. Click 'Create a new activity' to see what I'm trying to do.

Basically it's the same as Ember.Select [ ... ] multiple="true", but for checkboxes.

What's the correct approach for something like this with Ember.js?

Upvotes: 5

Views: 2615

Answers (1)

Marcio Junior
Marcio Junior

Reputation: 19128

You can use an itemController in your each view helper to manage the selection. In the code below I created one called ChildController:

App.ChildController = Ember.ObjectController.extend({    
    selected: function() {
        var activity = this.get('content');
        var children = this.get('parentController.children');
        return children.contains(activity);
    }.property(),
    selectedChanged: function() {
        var activity = this.get('content');
        var children = this.get('parentController.children');
        if (this.get('selected')) {                                    
            children.pushObject(activity);            
        } else {                                    
            children.removeObject(activity);                                                    
        }        
    }.observes('selected')
});

With a itemController you can expose some properties and logics, without add it directlly to your models. In that case the selected computed property and the selectedChanged observer.

In your template, you can bind the selection using checkedBinding="selected". Because the itemController proxy each model, the selected property of the itemcontroller will be used, and the {{name}} binding, will lookup the name property of the model:

<script type="text/x-handlebars" data-template-name="activities/new">
    <h1>Create a new activity</h1>

    {{#each childList itemController="child"}}
        <label>
            {{view Ember.Checkbox checkedBinding="selected"}}
            {{name}}
        </label><br />
    {{/each}}
    {{view Ember.TextField valueBinding="name"}}
    <button {{action create}}>Create</button>
</script>

The same aproach in edit template:

<script type="text/x-handlebars" data-template-name="activities/edit">
    <h1>Edit an activity</h1>

    {{#each childList itemController="child"}}
        <label>
            {{view Ember.Checkbox checkedBinding="selected"}}
            {{name}}
        </label><br />
    {{/each}}
    {{view Ember.TextField valueBinding="name"}}
    <button {{action update}}>Update</button>
</script>

This is a fiddle with this working http://jsfiddle.net/marciojunior/8EjRk/

Component version

Template

<script type="text/x-handlebars" data-template-name="components/checkbox-select">
    {{#each elements itemController="checkboxItem"}}
        <label>            
            {{view Ember.Checkbox checkedBinding="selected"}}
            {{label}}
        </label><br />
    {{/each}}    
</script>

Javascript

App.CheckboxSelectComponent = Ember.Component.extend({   
    /* The property to be used as label */
    labelPath: null,
    /* The model */
    model: null,
    /* The has many property from the model */
    propertyPath: null,
    /* All possible elements, to be selected */
    elements: null,
    elementsOfProperty: function() {
        return this.get('model.' + this.get('propertyPath'));
    }.property()
});

App.CheckboxItemController = Ember.ObjectController.extend({    
    selected: function() {        
        var activity = this.get('content');
        var children = this.get('parentController.elementsOfProperty');        
        return children.contains(activity);
    }.property(),
    label: function() {    
        return this.get('model.' + this.get('parentController.labelPath'));
    }.property(),
    selectedChanged: function() {
        var activity = this.get('content');
        var children = this.get('parentController.elementsOfProperty');
        if (this.get('selected')) {                                    
            children.pushObject(activity);            
        } else {                                    
            children.removeObject(activity);                                                    
        }        
    }.observes('selected')
});

Updated fiddle http://jsfiddle.net/mgLr8/14/

I hope it helps

Upvotes: 14

Related Questions