Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 695
Martin Avatar asked Oct 27 '13 13:10

Martin


1 Answers

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

like image 93
Marcio Junior Avatar answered Sep 28 '22 08:09

Marcio Junior