Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Backbone.js - How to create a collection that has bindable properties

This is somewhat of contrived example, but I believe it gets the point across.

Lets say I have a backbone cars collection. And for the collection I want a property called isValid. I want other objects to be able to bind to isValid and fire a function when isValid changes. The collection isValid property will be changed based on the models in a collection as a whole.

For example, if all the doors are locked on every car, then the isValid should change to true. When isValid changes, all functions that are bound to isValid change event should be fired.

My question is, how can I create a collection that has bindable properties that works similar to model properties?

This is the code that I would like get to work.

var Car = Backbone.Model.extend({});
var Cars = Backbone.Collection.extend({
    model: Car,
    url: "Cars",
    isValid: false,  //Here's the property that I want bindable
                     //but do not know how to accomplish this.
});

var cars = new Cars();

//Call a function when cars.isValid changes
cars.bind("change:isValid", carsIsValidChanged, cars)

//Not sure if this what the function would look like
//but would need access to cars.isValid or just the car collection
function carsIsValidChanged(isValid){
    console.log(isValid);
}


Update

See my complete solution as an answer below.

like image 301
Mike Barlow - BarDev Avatar asked Jan 27 '12 00:01

Mike Barlow - BarDev


1 Answers

So there is no 'built-in' way to respond to property changes on a Collection, as there really isn't a supported way (that I know of) to have properties on a collection. However, its still totally possible like this, I reckon. (untested, but should be fairly close)

var Car = Backbone.Model.extend({});
var Cars = Backbone.Collection.extend({
    model: Car,
    url: "Cars",
    initialize: function() {
        var self = this;
        this.isValid = false;
        this.bind('add', function() {
          var curValid = true;
          self.each(function(car) {
            if(car.get('is_locked') != true) {
              curValid = false;
            }
          });
          if(curValid != self.isValid) {
            self.isValid = curValid;
            self.trigger('change:isValid', self.isValid, self);
          }
        });   
    }                 
});

// instantiate a new Cars collection
var cars = new Cars();


function carsIsValidChanged(isValid){
    console.log(isValid);
}

//Call a function when cars.isValid changes
cars.bind("change:isValid", carsIsValidChanged)

One note: you don't want isValid: listed as property like you would model or url. This seems to make backbone be weird, and your isValid might span all instances of the collection. Its better to define them as an initializer, then each time you instantiate that collection you will properly have an instance of isValid accessible by this.isValid.

like image 70
spotman Avatar answered Oct 12 '22 22:10

spotman