Search code examples
javascriptember.jsember-cli

Observe when any Ember.ObjectController in Ember.ArrayController changes


I'm trying to set up mass-action checkboxes (for something like Delete Selected Items) in an Ember App. The idea is to make a mass-action dropdown show or hide if any one of the checkboxes are selected. I haven't put in the dropdown yet since I can't figure out how to observe all elements in the array. How can I:

  1. Set up the Array controller so it observes all client objects
  2. Get the number of checked items when it changes
  3. ALSO: Am I on track with the conventions of how I'm approaching the client itemController?

app/templates/clients.hbs

<section id="clients">
    <h4>my clients</h4>
    <ul>
    {{#each itemController="clients/client"}}
        <li>
            {{input type="checkbox" name="markedForDeletion" checked=markedForDeletion}}
            {{#link-to 'clients.show' this}}
                {{clientName}}
            {{/link-to}}
        </li>
    {{/each}}

    </ul>
</section>
{{#link-to 'clients.new'}}Add client{{/link-to}}
{{outlet}}

router.js

import Ember from 'ember';
import config from './config/environment';

var Router = Ember.Router.extend({
  location: config.locationType
});

export default Router.map(function() {
  this.resource('clients', function() {
    this.route('show', {path: '/:client_id'});
    this.route('new');
  });
});

app/routes/clients.js

import Ember from 'ember';
export default Ember.Route.extend({
    model: function() {
        return this.store.find('client');
    }
});

app/models/client.js

import DS from 'ember-data';
export default DS.Model.extend({
  clientName: DS.attr('string'),
  clientEmail: DS.attr('string')
});

app/controllers/clients.js

import Ember from 'ember';
export default Ember.ArrayController.extend({
    checkBoxesChanged: function() {
    // This fires only once, when the /clients/ route is activated
    console.log('markedForDeletion in array changed');
  }.observes('@each.clients')
});

app/controllers/clients/client.js

import Ember from 'ember';
export default Ember.ObjectController.extend({
    markedForDeletion: true,
    markedForDeletionChanged: function(){
        // This fires correctly
        console.log('markedForDeletion object changed');
    }.observes('markedForDeletion')
});

Edit: Similar question asked here, but I'm afraid the answers didn't help me all that much.


Solution

  • There are a couple of solutions to this. Since your solution only requires the need for an extra property of being checked, I think that an ObjectProxy will meet your needs. If, however, you needed more functionality, then a component would be a better fit.

    Note: Before we dive into the solution, though, it's important to note that ArrayController, ObjectController, and ItemController are all being deprecated.

    Since we won't be using itemController, you can remove app/controllers/clients/client.js

    app/templates/clients.hbs

    <section id="clients">
        <h4>my clients</h4>
        <ul>
        {{#each clientsWithMarker as |client|}}
            <li>
                {{input type="checkbox" name="markedForDeletion" checked=client.markedForDeletion}}
                {{#link-to 'clients.show' client}}
                    {{client.clientName}}
                {{/link-to}}
            </li>
        {{/each}}
    
        </ul>
    </section>
    {{#link-to 'clients.new'}}Add client{{/link-to}}
    {{outlet}}
    

    app/controllers/clients.js

    import Ember from 'ember';
    export default Ember.Controller.extend({
        clientsWithMarker: Ember.computed.map('model', function(client) {
            return Ember.ObjectProxy.create({
                content: client,
                checked: false
            });
        }),
    
        // This computed property returns an array of ObjectProxies that
        // are checked. It is recalculated automatically
        checkedClients: Ember.computed.filterBy('clientsWithMarker', 'checked', true),
    
        checkBoxesChanged: function() {
        // This fires only once, when the /clients/ route is activated
        console.log('markedForDeletion in array changed');
      }.observes('clientsWithMarker.@each.checked')
    });
    

    This should work, but I haven't actually tested this specific code.