Reputation: 3659
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
Reputation: 3230
Unfortunately I think this is due to a bug.
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.
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