Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Backbone Collection - Constructor

Tags:

backbone.js

I am passing an array to new collection. Is it possible to filter the array by passing second parameter and check on the attr of the object in the array and create collection only if it pass the filter test.

collection = new Backbone.Collection([{name:'xy',age:24},{name:'y',age:35}])

Can I create a collection only for the object less than 30 years.

like image 649
coool Avatar asked Aug 30 '13 11:08

coool


2 Answers

You can use _.filter on your array before passing into the collection

collection = new Backbone.Collection(
     _.filter([{name:'xy',age:24},{name:'y',age:35}], 
              function(item) { return item.age < 30; }));

Or you can move your custom filtering logic into your collection initialize where you will get the models array what you can modify/filter

var Under30 = Backbone.Collection.extend({
    initialize: function(models, options) {
        for(var i = models.length - 1; i > 0; i-- ){
            if (models[i].age > 30){
                models.splice(i,1);
            }
        }
    }
});

var collection = new Under30([{name:'xy',age:24}, {name:'y',age:35}]);
console.log(collection.models.length); // will be 1

Demo JSFiddle.

You can extend this with supplying the filter predicate in the options parameter

var FilteredCollection = Backbone.Collection.extend({
        initialize: function(models, options) {
            for(var i = models.length - 1; i > 0; i-- ){
                if (options.filter && options.filter(models[i])){
                    models.splice(i,1);
                }
            }
        }
    });

And use it with

var collection = new FilteredCollection(
      [{name:'xy',age:24}, {name:'y',age:35}], 
      {
          filter: function(item) { 
              return item.age > 30; 
          }
      }
);
console.log(collection.models.length); // will be 1
like image 93
nemesv Avatar answered Jan 02 '23 18:01

nemesv


Update

You're interested in validation before creating the collection, just like the answer before - we'll create a function that lets you reuse that strategy to validate any collection based on a condition you'd like.

//A collection that validates the correctness of the given elements
// based on a predicate (condition). If the items do not _all_ pass
// the condition - the collection will not be created and an error will
// be thrown.
function validatingCollection(predicate){
    return function(arr){
        //first we assert an array is passed if it's possible to assert
        if(Array.isArray && !Array.isArray(arr)){
            throw new Error("Can only pass array to filteredCollection");
        }
        //for older browsers - use _.every 
        // now we validate they all pass the condition
        if(!arr.every(predicate){
            throw new Error("Validation error, not all elements pass");
        }
        //they all pass, create the collection
        return new Backbone.Collection(arr);
    };
};

Now, we can create such a collection, that validates that every element has age of at least 30:

var ageValidated = validatedCollection(function(obj){ return obj.age < 30; });
//create a new age validatedcollection
try{
    var collection = new ageValidated([{name:'xy',age:24},{name:'y',age:35}]); 
}catch(e){
    // the above throws an error because of the object with age 24
}
//however - this works, and creates a new collection
var collection = new ageValidated([{name:'xy',age:32},{name:'y',age:35}]); 

If you just want to filter the elements you don't like - instead of throwing an error if not all of them pass validation you can do something like this :

collection = new Backbone.Collection([{name:'xy',age:24},
                                      {name:'y',age:35}].filter(function(obj){
                                          return obj.age < 30;
                                      });

Alternatively, you can create your own filtered collection constructor for reuse as such

function filteredCollection(predicate){
    return function(arr){
        //first we assert an array is passed if it's possible to assert
        if(Array.isArray && !Array.isArray(arr)){
            throw new Error("Can only pass array to filteredCollection");
        }
        return new Backbone.Collection(arr.filter(predicate));
    };
};

Which lets us do :

var ageFilter = filteredCollection(function(obj){ return obj.age < 30; });
    //create a new age collection
var collection = new ageFilter([{name:'xy',age:24},{name:'y',age:35}]); 

The other answer suggests _.filter over the native Array.filter , this might be beneficial if you have to support old browsers.

like image 33
Benjamin Gruenbaum Avatar answered Jan 02 '23 18:01

Benjamin Gruenbaum