blessanm86
blessanm86

Reputation: 31779

Ember computed.sort fails after setting sortProperties multiple times

I am trying to implement sorting + filtering into my array controller. Everything is working except my template doesn't seem to get the computed properties after a change the sort direction more than 2 times. Im not sure whats really going wrong or if its a bug.

Here is the relevant code and a failing bin(click the sort button multiple times, the template stops showing the computed properties from the item controller).

{{#each sortedContent}}
  <li>{{title}} - {{symbol}}{{totalAmount}}</li>
{{/each}}

App.IndexController = Em.ArrayController.extend({

  itemController: 'item',

  filterText: '',

  sortProperties: ['totalAmount:asc'],
  sortedContent: Em.computed.sort('filteredContent', 'sortProperties'),

  filteredContent: function() {
    console.log('filter');
    return this.filter(function(item) {
      return item.get('title').indexOf(this.get('filterText')) >= 0;
    }.bind(this));
  }.property('filterText','@this'),

  actions: {
    sortBy: function() {
      var direction = 'asc';
      if(this.get('sortProperties.0').split(':')[1] === 'asc') {
        direction = 'desc'; 
      }
      console.log('totalAmount:'+direction);
      this.set('sortProperties', ['totalAmount:'+direction]);
    }
  }

});

App.ItemController = Em.ObjectController.extend({
  totalAmount: Em.computed.alias('price.amount'),
  symbol: Em.computed.alias('price.currency')
});

Upvotes: 3

Views: 1115

Answers (2)

Matthew Blancarte
Matthew Blancarte

Reputation: 8301

Took me a while of staring at it, but I knew it was something small.

Your sorted content and filtered content are both computed, so you need to make sure they are both watching each other for changes. If you bind sortProperties to filteredContent, it will work.

property('filterText', '@this', 'sortProperties')

Working JSBin: http://emberjs.jsbin.com/bicepobu/1/edit

I realized that the collection was still alive and no errors were being thrown when I started entering filters into the text field and it was rendering, then I could click twice and bug it out again...

Computed property errors are pretty much the equivalent of the old "forgotten semi-colon". I swear that 9/10 bugs in my own Ember code are from this sort of thing.

Update::

Okay, so I think I can better describe what's happening. You are feeding in the filteredContent to the macro. That macro is removing and adding items to your filtered content array. If you think about it, what's happening makes sense.

Take a look at this line: https://github.com/emberjs/ember.js/blob/v1.6.1/packages_es6/ember-runtime/lib/computed/reduce_computed_macros.js#L756

return arrayComputed(itemsKey, { //...
  1. You hit the button to run the sort macro.
  2. The sort macro rips apart and reconstructs the filteredContent, returning a new collection. This is where filteredContent loses sight!
  3. When you hit the filter button the second time, it's trying to access the original filteredContent, which was ripped apart and was never updated with the new model! (thus, the need to watch sortedProperties for changes). It worked the first time because it had a healthy origin array to work from. The second time, it was grabbing at "thin air" (empty array).

If your custom filter property isn't watching for changes to sortProperties, it doesn't know to re-establish itself after being mangled in the macro.

Yikes! Certainly don't hold it against the Ember guys.

It would be nice if the sort macro just returned the same collection and not a brand new one... There is probably a really good reason for this.

Upvotes: 4

Kingpin2k
Kingpin2k

Reputation: 47367

It looks like Ember, or something else, hates your item controller. My working guess/theory is it disconnects from the item controller at some point, and then all of your sorting and other labels break (due to them being reliant on the item controller)

http://emberjs.jsbin.com/jakax/1/edit

I'm not sure exactly what the issue is, but I'd probably just wrap it in a normal Ember Object (depending on what you're trying to do).

App.Foo = Em.Object.extend({
  totalAmount: Em.computed.alias('price.amount'),
  symbol: Em.computed.alias('price.currency')
});

http://emberjs.jsbin.com/jakax/2/edit

Upvotes: 2

Related Questions