Backbone collection sortBy

2019-06-23 18:26发布

问题:

I make my first backbone app and get some problems with collection sorting. After using this

var SortedFriends = MyFriends.sortBy(function(friend) {
          return friend.get("uid");
        });  

console.log(SortedFriends) show that SortedFriends contains sorted models, but when i try to use collection functions like 'SortedFriends.each' or 'SortedFriends.at' it make error:

TypeError: SortedFriends.each is not a function.

Code:

  var Friend = Backbone.Model.extend({});          
      var Friends = Backbone.Collection.extend({
        model: Friend,            
      });  

      var MyFriends = new Friends();
      MyFriends.reset(<?=$friends?>);

      var FriendView = Backbone.View.extend({
          initialize: function(){
              model:Friend            
          },              
          tagName: "tr",
          template: _.template($('#item-template').html()),
          className: "document-row",          
          render: function() {                              

         this.$el.html(this.template(this.model.toJSON()));                            
              return this;             
          }          
      });     


    var SortedFriends = MyFriends.sortBy(function(friend) {
      return friend.get("uid");
    });          

    var addOne = function(element){           
        var view = new FriendView({model: element});
        $("#friends").append(view.render().el);
    }                              
    console.log(JSON.stringify(SortedFriends));
    SortedFriends.each(function(friend){
        var view = new FriendView({model: friend});
        $("#friends").append(view.render().el);            
    });

回答1:

If youre using backbone collections then youre probably better off using the comparator rather than collection methods

http://backbonejs.org/#Collection-comparator

When youre ready to sort your collection:

MyFriends.comparator = function(friend){
   return friend.get("uid");
});
MyFriends.sort();

OR if you want to keep the order of the unsorted collection then you will need to clone it first

http://backbonejs.org/#Collection-clone

var SortedFriends = MyFriends.clone();
SortedFriends.comparator = function(friend){
   return friend.get("uid");
});
SortedFriends.sort();


回答2:

I'm not sure if it's a bug or a feature of Backbone's adaptation of sortBy, but apparently it returns an array, not an Underscore collection.

One workaround is to wrap the whole thing in _( ... ), which tells Underscore to wrap the array back into a collection:

var SortedFriends = _(MyFriends.sortBy(function(friend) {
  return friend.get("uid");
}));

Edit

Most of the Underscore methods in Backbone seem to be chainable (replace sortBy with reject, for example, and it runs). Looking at the Backbone source where they wire up the Underscore proxies, it seems that sortBy is treated differently. I can't understand why they do it this way ...

var methods = ['forEach', 'each', 'map', 'collect', 'reduce', 'foldl',
    'inject', 'reduceRight', 'foldr', 'find', 'detect', 'filter', 'select',
    'reject', 'every', 'all', 'some', 'any', 'include', 'contains', 'invoke',
    'max', 'min', 'toArray', 'size', 'first', 'head', 'take', 'initial', 'rest',
    'tail', 'drop', 'last', 'without', 'indexOf', 'shuffle', 'lastIndexOf',
    'isEmpty', 'chain'];

_.each(methods, function(method) {
    Collection.prototype[method] = function() {
      var args = slice.call(arguments);
      args.unshift(this.models);
      return _[method].apply(_, args);
    };
});

var attributeMethods = ['groupBy', 'countBy', 'sortBy'];

_.each(attributeMethods, function(method) {
    Collection.prototype[method] = function(value, context) {
      var iterator = _.isFunction(value) ? value : function(model) {
        return model.get(value);
      };
      return _[method](this.models, iterator, context);
};