4

I've an individualStore (extends from Em.ArrayController), whose task is to keep an array of individual objects. There are several APIs that my application calls, and they return individual objects which are sent to the store. Think about it as the database of cached individual records in my application.

App.individualStore = App.ArrayController.create({

  allIndividuals: function () {
    return this.get('content').sort(function (a, b) {
      return (b.votes_count - a.votes_count);
    });
  }.property('@each.votes_count').cacheable(),

  aliveIndividuals: function () {
    return this.get('content').filter(function (individual) {
      return (!!individual.living);
    }).sort(function (a, b) {
      return (b.votes_count - a.votes_count);
    });
  }.property('@each.living', '@each.votes_count').cacheable(),

  deceasedIndividuals: function () {
    return this.get('content').filter(function (individual) {
      return (!individual.living);
    }).sort(function (a, b) {
      return (b.votes_count - a.votes_count);
    });
  }.property('@each.living', '@each.votes_count').cacheable()

});

My view has a `individualsBinding: 'App.individualStore.allIndividuals', which renders up as intended perfectly.

I want to add filtering buttons, e.g. Show: All | Alive | Deceased. What would be the right way to change the filtering here? Keep in mind that whatever the criteria is, I'd like it to keep in sync with individualStore always.

Someone suggested to change bindings on runtime,

this.bind('users', Ember.Binding.from('App.individualStore.aliveIndividuals'));

This works in my first two-three clicks on these buttons, but then it freezes the browser (sort of infinite loop?).

This also doesn't feel like the best option to me. I'm new to ember, so anything you say would be helpful. Thanks in advance.

Kazim Zaidi
  • 524
  • 4
  • 19

1 Answers1

7

I would make the filter function itself a property and by changing a filterName on the controller, you are notified and accordingly update the filtered content, see http://jsfiddle.net/pangratz666/ypcLq/

App.controller = Ember.ArrayProxy.create({
    content: [],

    filterName: 'all',

    allFilter: function() {
        return true;
    },
    aliveFilter: function(individual) {
        return ( !! individual.living);
    },
    deceasedFilter: function(individual) {
        return (!individual.living);
    },

    filtered: function() {
        var filterName = this.get('filterName');
        var filterFunc = this.get(filterName + 'Filter');

        return this.filter(filterFunc).sort(function(a, b) {
            return (b.votes_count - a.votes_count);
        });
    }.property('content.@each', 'filterName').cacheable()

});

So you can later in your view set the filter which shall be used via App.controller.set('filterName', 'alive').

Just as a note: you can chain filters via this.filter(filterFunc1).filter(filterFunc2) so you could for example filter all alive individuals of a specific age, ...

pangratz
  • 15,875
  • 7
  • 50
  • 75
  • Thanks for the great answer.. this is a much better way than what I was doing. I've one trouble though. I want to keep `filterName` and `filtered` properties inside my `peopleController`, which is distinct from individualStore. This is because I want other controllers to use individualStore in a similar way too. In that case, I'd need something like `.property('App.individualStore.content.@each', 'App.individualStore.content.@each.living')` etc. It seems ember doesn't like such observers. It gives me an error saying `a is undefined`. Is there a cleaner way to separate concerns? – Kazim Zaidi Mar 28 '12 at 17:38
  • Great, I got this done. The error was due to an undefined filter. I've grouped the filters into my `individualStore.filters` namespace, and my sort functions inside `individualStore.sorts` namespace, and my peopleController appropriately connects them! The code is much cleaner, thanks to you @pangratz! – Kazim Zaidi Mar 28 '12 at 18:22
  • I'm glad I could help! Nice to see! – pangratz Mar 28 '12 at 18:23