最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Ember - sort array by multiple properties in multiple directions - Stack Overflow

programmeradmin3浏览0评论

I need to sort a collection of Ember Models by multiple properties, and not necessary in the same direction/order. I.e. I need to sort by property a in ascending order and by property b in descending. Is there a way to achieve this?

Update

I tried setting the sortAscending property to a array, but it is not working. After looking into the source it seems that this functionality is not supported out of the box(yet).

I need to sort a collection of Ember Models by multiple properties, and not necessary in the same direction/order. I.e. I need to sort by property a in ascending order and by property b in descending. Is there a way to achieve this?

Update

I tried setting the sortAscending property to a array, but it is not working. After looking into the source it seems that this functionality is not supported out of the box(yet).

Share Improve this question edited Dec 31, 2014 at 16:28 laurent 7759 silver badges14 bronze badges asked Feb 21, 2013 at 9:51 Shimon RachlenkoShimon Rachlenko 5,51742 silver badges51 bronze badges 0
Add a comment  | 

3 Answers 3

Reset to default 15

In your ArrayController:

sortProperties: ["propA:asc", "propB:desc"]
sortedModel: Ember.computed.sort("model", "sortProperties");

Then reference sortedModel in your template's #each handler.

I decided to create a mixin which allows sorting in multiple orders(directions). It extends the SortableMixin trying to as much backwards-compatible as possible. Actually it can be used as regular SortableMixin. What it adds is the sortAscendingProperties property which is array of sort property names(members of sortProperty array) which should be sorted in ascending order. If the property is in sortAscendingProperties it will be sorted in ascending order, otherwise it will be sorted according to sortAscending, which serves as kind of default.
I called the mixin the MultiSortableMixin although I think it is not the best name.

(function() {

    var get = Ember.get, set = Ember.set, forEach = Ember.EnumerableUtils.forEach;

    /**
     * extends the SortableMixin by allowing sorting by multiple orders.
     *
     * sortProperties - array of strings
     * sortAscending - default sort order
     * sortAscendingProperties - properties which are listed here will be sorted in ascending order, those which are not listed - will be sorted according to sortAscending
     */
    Ember.MultiSortableMixin = Ember.Mixin.create(Ember.SortableMixin, {
        /**
         Specifies the arrangedContent's sort direction

         @property {Array} sortAscendingProperties
         */
        sortAscendingProperties: null,
        orderBy: function(item1, item2) {
            var result = 0,
                    sortProperties = get(this, 'sortProperties'),
                    sortAscending = get(this, 'sortAscending'),
                    sortAscendingProperties = get(this, 'sortAscendingProperties');

            Ember.assert("you need to define `sortProperties`", !!sortProperties);

            forEach(sortProperties, function(propertyName) {
                if (result === 0) {
                    result = Ember.compare(get(item1, propertyName), get(item2, propertyName));
                    //use default sortAscending if propertyName is not listed in sortAscendingProperties
                    var sa = (sortAscendingProperties && sortAscendingProperties.indexOf(propertyName) > -1) || sortAscending;
                    if ((result !== 0) && !sa) {
                        result = (-1) * result;
                    }
                }
            });

            return result;
        },
        //overrided to add more watched props. TODO - the contents of this method is the same as parent's - find the way to just add watched stuff
        arrangedContent: Ember.computed('content', 'sortProperties.@each', 'sortAscendingProperties.@each', 'sortAscending', function(key, value) {
            var content = get(this, 'content'),
                    isSorted = get(this, 'isSorted'),
                    sortProperties = get(this, 'sortProperties'),
                    self = this;

            if (content && isSorted) {
                content = content.slice();
                content.sort(function(item1, item2) {
                    return self.orderBy(item1, item2);
                });
                forEach(content, function(item) {
                    forEach(sortProperties, function(sortProperty) {
                        Ember.addObserver(item, sortProperty, this, 'contentItemSortPropertyDidChange');
                    }, this);
                }, this);
                return Ember.A(content);
            }

            return content;
        }),
    // unneeded in this mixin, overrided to disable functionality from SortableMixin. TODO - find a way to just remove it
        sortAscendingDidChange: Ember.observer(function() {
            //empty
        }, 'sortAscending')
    });

})();

Usage example:

App.ThingsController = Ember.ArrayController.extend(Ember.MultiSortableMixin, {
    sortProperties: ['prop1', 'prop2', 'prop3'], 
    sortAscending: false,
    sortAscendingProperties: ['prop2', 'prop3'], 
    //your stuff
});

In this example the content of ThingsController will be sorted by prop1 - in descending order, then by prop2 and prop3 - both in ascending order.

This is no out of the box functionality of Ember. But looking at the code of SortableMixin one can see that it uses Ember.compare to compare two entities:

orderBy: function(item1, item2) {
    var result = 0,
        sortProperties = get(this, 'sortProperties'),
        sortAscending = get(this, 'sortAscending');

    Ember.assert("you need to define `sortProperties`", !!sortProperties);

    forEach(sortProperties, function(propertyName) {
      if (result === 0) {
        result = Ember.compare(get(item1, propertyName), get(item2, propertyName));
        if ((result !== 0) && !sortAscending) {
          result = (-1) * result;
        }
      }
    });

    return result;
  },

And Ember.compare contains a check on the Comparable Mixin:

var Comparable = Ember.Comparable;
  if (Comparable) {
    if (type1==='instance' && Comparable.detect(v.constructor)) {
      return v.constructor.compare(v, w);
    }

    if (type2 === 'instance' && Comparable.detect(w.constructor)) {
      return 1-w.constructor.compare(w, v);
    }
  }

Therefore my proposed solution is:

1 - Add an additional field to your models which contains a wrapper object of all your sortingProperties, e.g. "combinedAandB"

App.YourModel = Ember.Object.extend({
a : null,
b : null,
combinedAandB : function(){
  // the following Object should implement SortableMixin
  var comparator = App.AandBComparator.create(this.get("a"), this.get("b"));
  return comparator;
}.property("a","b")

2 - Your ComparatorModel (App.AandBComparator) should implement the Comparable Mixin. Inside this comparation method respect your desired sorting behaviour (prop a ascending and prop b descending).

3 - Now you could instantiate an ArrayController and sort it based on your combined property:

var yourModelController = //whereever you may get that one from
yourModelController.set("sortProperties", "combinedAandB");

Note: This is just a spontaneous idea i got when reading your requirement. I have not yet implemented this. So this is likely not perfect :-)

发布评论

评论列表(0)

  1. 暂无评论