Backbone.js: correct way of filtering a collection

2019-02-16 20:12发布

问题:

The current method I'm using is to filter a collection, which returns an array, and use

collection.reset(array)

to re-populate it. However, this modifies the original collection, so I added an array called "originalCollectionArray" which keeps track of the initial array state of the collection. When no filtering is active I simply use

collection.reset(originalCollectionArray)

But then, I need to keep track of adding and removing models from the real collection, so I did this:

// inside collection
initialize: function(params){
    this.originalCollectionArray = params;
    this.on('add', this.addInOriginal, this);
    this.on('remove', this.removeInOriginal, this);
},
addInOriginal: function(model){
    this.originalCollectionArray.push(model.attributes);
},
removeInOriginal: function(model){
    this.originalTasks = _(this.originalTasks).reject(function(val){
        return val.id == model.get('id');
    });
},
filterBy: function(params){
    this.reset(this.originalCollectionArray, {silent: true});
    var filteredColl = this.filter(function(item){
        // filter code...
    });
    this.reset(filteredColl);
}

This is quickly becoming cumbersome as I try to implement other tricks related to the manipulation of the collection, such as sorting. And frankly, my code looks a bit hacky. Is there an elegant way of doing this?

Thanks

回答1:

You could create a collection as a property of the main collection reflecting the state of the filters:

var C = Backbone.Collection.extend({
    initialize: function (models) {
        this.filtered = new Backbone.Collection(models);
        this.on('add', this.refilter);
        this.on('remove', this.refilter);
    },

    filterBy: function (params){
        var filteredColl = this.filter(function(item){
          // ...
        });

        this.filtered.params = params;
        this.filtered.reset(filteredColl);
    },

    refilter: function() {
        this.filterBy(this.filtered.params);
    }
});

The parent collection keeps its models whatever filters you applied, and you bind to the filtered collection to know when a change has occurred. Binding internally on the add and remove events lets you reapply the filter. See http://jsfiddle.net/dQr7X/ for a demo.



回答2:

The major problem on your code is that you are using a raw array as original, instead of a Collection. My code is close to the yours but use only Collections, so methods like add, remove and filter works on the original:

  var OriginalCollection = Backbone.Collection.extend({
  });
  var FilteredCollection = Backbone.Collection.extend({
    initialize: function(originalCol){
        this.originalCol = originalCol;
        this.on('add', this.addInOriginal, this);
        this.on('remove', this.removeInOriginal, this);
    },
    addInOriginal: function(model){
        this.originalCol.add(model);
    },
    removeInOriginal: function(model){
        this.originalCol.remove(model);
    },
    filterBy: function(params){
        var filteredColl = this.originalCol.filter(function(item){
            // filter code...
        });
        this.reset(filteredColl);
    }   
  });