8

I have a Ember.ArrayController that has an unsorted content.

I want to know if its possible to sort the content of an ArrayController without using a new property.

I could of course create a new property:

App.MyArrayController = Em.ArrayController.extend({
  mySortMethod: function(obj1, obj2) {
    // some code here
  },
  updateSortedContent: function() {
    var content = this.get('content');
    if (content) {
      var sortedContent = content.copy();
      sortedContent.sort(this.mySortMethod);
      this.set('sortedContent', sortedContent);
    }
  }.observes('content')
});

But I hope there is a better way that does not duplicates the content.

1

2 Answers 2

35

UPDATE

The latest version of Ember actually has sorting built in. ArrayController now includes the Ember.SortableMixin which will engage if you specify sortProperties (Array) and optionally sortAscending (Boolean).

Note: with the new SortableMixin, you still need to refer to arrangedContent to get the sorted version. The model itself will be left untouched. (Thanks to Jonathan Tran)

App.userController = Ember.ArrayController.create({
  content: [],
  sortProperties: ['age'],
  sortAscending: false
})

ORIGINAL ANSWER

The correct way to do this is to use the arrangedContent property of ArrayProxy. This property is designed to be overridden to provide a sorted or filtered version of the content array.

App.userController = Ember.ArrayController.create({
  content: [],
  sort: "desc",
  arrangedContent: Ember.computed("content", function() {
    var content, sortedContent;
    content = this.get("content");
    if (content) {
      if (this.get("sort") === "desc") {
        this.set("sort", "asc");
        sortedContent = content.sort(function(a, b) {
          return a.get("age") - b.get("age");
        });
      } else {
        this.set("sort", "desc");
        sortedContent = content.sort(function(a, b) {
          return b.get("age") - a.get("age");
        });
      }
      return sortedContent;
    } else {
      return null;
    }
  }).cacheable()
});
Sign up to request clarification or add additional context in comments.

5 Comments

Defining a sortProperty sorts by default. Can this is changed to adapt sorting only when a user request is registered(through a dropdown or something) ?
You define the default sort properties and ascending to be whatever is "normal" to your content. Then you make a little action that changes those two properties via buttons or drop-downs and magic happens. I have a reSort method on the controller that sets the sort property (I use only one) to what the button sends as an argument. And if the property is the same as the one already set it interprets that as an indication to reverse the order to descending. gist.github.com/3744939
Note: with the new SortableMixin, you still need to refer to arrangedContent to get the sorted version. This tripped me up at first.
@JonathanTran Could you please explain, what do you mean by 'you still need to refer to arrangedContent'? Where do I need to refer it?
@SatA In your template, or wherever you need the sorted version, don't use content. That is the original model array and won't be sorted.
2

You have to manually sort arrays, including any array controller's content, in Ember. You could always replace content with your sorted array instead of keeping both arrays.

Update

Here is an example of what I think you're looking for: http://jsfiddle.net/ud3323/yjs8D/

Update #2

Updated this example to use the new view context changes. https://gist.github.com/2494968

Handlebars

<script type="text/x-handlebars" >
Will Display and Sort by Age (after 2 sec)<br/><br/>

{{#each App.userController}}
    {{#view App.RecordView}}
        {{name}} - {{age}}
    {{/view}}
{{/each}}
</script>

JavaScript

App = Ember.Application.create({
    ready: function() {
        Ember.run.later(this, function() {
            Ember.run.later(this, function() {
                App.get('userController').set('content', [
                   Ember.Object.create({ name:"Jeff", age:24 }),
                   Ember.Object.create({ name:"Sue", age:32 }),
                   Ember.Object.create({ name:"Jim", age:12 })
               ]);
      }, 2000);
        Ember.run.later(this, function() {
            // Make the controller's content sort again in reverse this time
            App.userController.notifyPropertyChange('content');
        }, 4000);
    }
});

App.userController = Ember.ArrayController.create({
    content: [],

    contentDidChange: Ember.observer(function(userCtr, prop) {
        if(!Ember.empty(this.get('content'))) {
            console.log('about to begin sort');
            this.sortContent();
        }

        this._super();
    }, 'content'),

    sort:"desc",

    sortContent:function() {
        var content = this.get("content"), sortedContent;

        if (this.get("sort") == "desc") {
            this.set("sort", "asc");
            sortedContent = content.sort( function(a,b){
                return a.get("age") - b.get("age");
            });
        } else {
            this.set("sort","desc");
            sortedContent = content.sort( function(a,b){
                return b.get("age") - a.get("age");
            });
        }

        this.set("content",sortedContent);
    }
});

App.RecordView = Ember.View.extend({});

3 Comments

yep, but I do not know where to replace it, because if the 'content' observer replace the content, the callback will be sent again, and again, and again,...
it works! I already tried that but I called this._super() before sorting the content so it did not work. Anyway, thank you
Yeah that's an undocumented little Ember gotcha. You have to call _super() at the end because contentDidChange defined in Ember.ArrayProxy will add in the observers on the content property that Ember.ArrayProxy needs. So the trick is that you want to change the content before you add those observers or you'll hit the infinite loop problem it sounds like you're already experienced ;)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.