elsurudo
elsurudo

Reputation: 3659

ArrayController's "arrangedContent" not always updated when "sortAscending" is set

I'm trying to set up dynamic sorting in my Ember.ArrayController subclass using one HTML select tag as follows:

App.ListController = Ember.ArrayController.extend
    sortOptions: [{id: 0, text: 'Price High-Low', sortBy: 'pricing', sortAsc: false},
              {id: 1, text: 'Price Low-High', sortBy: 'pricing', sortAsc: true},
              {id: 2, text: 'Name Ascending', sortBy: 'name', sortAsc: true},
              {id: 3, text: 'Name Descending', sortBy: 'name', sortAsc: false}]
    currentSortOptionId: 0

    sortBy: (->
        this.get('sortOptions')[this.get('currentSortOptionId')].sortBy
    ).property('currentSortOptionId')

    # Override
    sortProperties: (->
        [this.get('sortBy')]
    ).property('sortBy')

    # Override
    sortAscending: (->
        this.get('sortOptions')[this.get('currentSortOptionId')].sortAsc
    ).property('currentSortOptionId')

In my template, I've got:

Sort by {{view Ember.Select content=sortOptions
                                    optionValuePath="content.id"
                                    optionLabelPath="content.text"
                                    value=currentSortOptionId}}

{{#each listing in controller.arrangeContent}}
    ...
{{/each}}

Changing the selector works most of the time, and always works in switching the sort property. However, the sort direction (via "sortAscending") property gets confused, and seems to sometimes get one action behind (ie. uses the previously selected value for "sortAscending").

What could be going on here?

EDIT: Here's a JSFiddle of an isolated example: http://jsfiddle.net/s9AFr/3/

If you change the sort selector a few times, you can convince yourself that it's sorting incorrectly some of the time, especially when it comes to the ascending/descending feature of the sort. The sort seems to "lag" behind the user's choice.

Upvotes: 2

Views: 873

Answers (1)

Sherwin Yu
Sherwin Yu

Reputation: 3230

Unfortunately I think this is due to a bug.

Why it happens

Basically what happens is right now Ember doesn't expect both sortProperties and sortAscending to change at the same time. In the SortableMixin's sortAscendingWillChange observer, we keep track of the old value for sortAscending (storing it in _lastSortAscending), and then, in the after observer, if sortAscending did indeed, they simply flip the arrangedContent array:

sortAscendingDidChange: Ember.observer('sortAscending', function() {
  if (get(this, 'sortAscending') !== this._lastSortAscending) {
    var arrangedContent = get(this, 'arrangedContent');
    arrangedContent.reverseObjects();
  }
}),

The problem arises when you set sortAscending and sortProperties at the same time, as the sortAscendingWillChange before observer fires, keeping rack of the OLD sortAscending property, then because sortProperties changed, the array will get re-sorted, using the NEW value of sortAscending (so at this point, everything is sorted as you expect).... but then finally the sortAscendingDidChange observer fires, sees that sortAscending is different, so it flips the entire array again.

EDIT: Work around (JSfiddle)

Given the cause of this bug, I think the workaround is to make sure sortAscending and sortProperties don't change simultaneously.

So instead of making sortAscending a computed property, we can set it in an observer that observes currentSortOptionId, taking care to set it in the next run loop (so we use Ember.run.later). This way, the sortProperties computed property will change first, the content will get sorted, and then the sortAscending property will be set afterward.

Upvotes: 3

Related Questions