Replacing SortableMixin in 2.0


#1

Hello,

So it appears SortableMixin disappeared from Ember 2.0.

It came out as a surprise as there is nothing on it in 1.x deprecations, nor in any of the individual release notes. It did not raise a deprecation warning in my project either. Only thing I could find is the commit that says “Remove Ember.SortableMixin” and that’s it. Even searching the web for “SortableMixin ember deprecated” yields no result. Actually, the latest topics mentionning it on this very forum still recommend using it.

So, alright then, what is the migration path for that? I hear there is a Ember.computed.sort, but it is nowhere as versatile. Especially, I need the following features that were available with SortableMixin:

  1. dynamically changing the sorting key.
  2. using multiple keys for splitting ties (people with same name are sorted by birth date).
  3. dynamic re-ordering when item properties change.
  4. not re-sorting the whole array at every insertion. It has a couple thousand items.

Basically, what I used to get with SortedArray = Ember.ArrayProxy.extend(Ember.SortableMixin)

From what I have seen, 1 and 2 can be achieved using a custom sort function. However, when doing so, there is no way to get the 3, as Ember.computed.sort does not setup observers when given a custom sort function.

I could define a manual property but, again, as dependency declaration is static, there is no way to get both 1) and 3). I guess I could manage observers and bindings manually but then I’m just reimplementing my own SortableMixin.

It would be great if someone had:

  • A few hints of what the thinking behind the removal of SortableMixin was?
  • Any clue on the Ember 2.0 way to reach the same goal?
  • As my default approach would be to re-implement something similar starting from ArrayProxy, it would be great to know if it’s here to stay, or if I may find it gone soon? I rely on it quite a lot as I find it convenient to encapsulate and abstract out array transformations.

Thanks for your time.


#2

Your first two are doable with the ember.computed.sort. The documentation kinda sucks on it but if you look closely it takes a property by name and corresponds to an array of sort options. That property can be a computed property that you can dynamically generate.

For example it could return. [‘name:desc’,‘birthdate:asc’]

edit: number 3 can be achieved by making the above property dependent on the changing properties I think.


#3

Thanks for your answer. So it means I can to this then…

export default Ember.Component.extend({
    books: null,      // an array of Ember.Objects
    sortKeys: ['title', 'author'],
    sortedBooks: Ember.computed.sort('books', 'sortKeys')
});

From my understanding of the code of Ember.computed.sort, it should then add observers automatically on books for whatever keys are in sortKeys. So far so good, I’m getting 1) to 3) then.

3 issues down, 1 left, a few new:

  • First one is performance. Inserting an object in the array just went from O(n) to O(n⋅log(n)). That, and the fact that all observers are torn down and added again verytime anything changes. Given my use is mostly single pushObject calls on a large array, it seems very costly (I do client-side pagination for a couple thousand items). But I guess I can live with that and hope it gets improved at some point.

  • Ember.computed.sort does not seem to have an equivalent to the old SortableMixin.sortFunction. The comparison is hard-coded to use Ember.compare, which usually does a great job. I however need to tweak string comparison to use locale options, namely enabling the -kn collation so numbers get sorted naturally (“foo1” < “foo2” < “foo10”). But then maybe I should just monkey-patch Ember.compare, as I need that everywhere in my project.

  • Reversing the sort is somewhat complicated. We now have to go through the sortKeys property, split each key on ':', swap “desc” for “asc” and vice versa, then rebuild the keys. Am I missing something, or does what used to be a single toggleProperty call now require an array mapping?

I’m still a bit disappointed. I used to do:

var SortedArray = Ember.ArrayProxy.extend(Ember.SortableMixin);

// then in some component:
sortedBooks: function () {
    return SortedArray.create({content: get(this, 'books')});
}.property('books')

… and it seems Ember.computed.sort is somewhat of a regression, both feature-wise and performance-wise.


#4

SortableMixin was extracted to an addon along with the ArrayController stuff, you could use that: https://github.com/cibernox/ember-proxy-controllers


#5

I think the official addon is (or will be) https://github.com/emberjs/ember-legacy-controllers


#6

Great! It’s nice to know they will get an official addon. In the meantime I went for a building a small SortedArrayProxy, that does not use before observers. I reused the binary search from the old SortableMixin.

I really like using ArrayProxy for array transforms. They’re both efficient and explicit, and can be composited easily. I think Ember.computed.sort could just do that behind the scenes.

Thanks to all for the answers.


#7

Do you have any chance of sharing what you’re using now?


#8

Hey Oskar,

I answered a similar question recently on StackOverflow with this Ember-Twiddle. TL;DR; You can easily use Ember.computed.sort to functionally replace Ember.SortableMixin. I hope that’s helpful.

I’m also going to shamelessly plug my addon ember-computed-sortby. This addon makes sorting easier if you just need to consistently immutable sort something; IE never change the sort order.

– Wesley


#9

@Oskar> I would strongly recommend using Ember.computed.sort unless you have one of the following issues:

  • You need a custom sort function. Though Ember.computed.sort kind of supports it, it will not update properly so it is pretty much useless. The reason is there is no way to tell sort what observers to set on the array items.
  • You have a large number of items, only perform small changes (add one item, remove one item), and hit perfomance issues as Ember.computed.sort resorts the whole array everytime.

I hope those issues get fixed right in Ember.computed.sort at some point. In the meantime, unfortunately you have to do it yourself. What I did it get some inspiration from https://github.com/emberjs/ember-legacy-controllers, and build my own SortedArray. I’m not that fond of the idea of putting what I am using online, because 1) making it a proper module with testing, ember-cli support and stuff is quite an endeavor and 2) I’d rather see the issues fixed than have a parallel feature spread.

If despite all this you still want me to dump my file on a gist, just say so, I feel I gave proper warnings now.


#10

Why has there been no movement on this PR?

https://github.com/emberjs/ember.js/pull/12471


#11

I don’t know. I suppose I can bump in the PR.


#12

Likely because no one has had time to review and test it out. Feel free to test and let us know if it works for you.