2012-01-27 17 views
8

Esto es un ejemplo un tanto artificial, pero creo que hace entender el punto.Backbone.js - Cómo crear una colección que tenga propiedades enlazables

Digamos que tengo una colección de autos de la red troncal. Y para la colección, quiero una propiedad llamada isValid. Quiero que otros objetos puedan vincularse a isValid y activar una función cuando cambieValid. La propiedad collectionValid se cambiará según los modelos de una colección en conjunto.

Por ejemplo, si todas las puertas están bloqueadas en cada automóvil, entonces isValid debería cambiar a verdadero. Cuando isValid cambia, todas las funciones que están vinculadas al evento de cambio isValid deben activarse.

Mi pregunta es, ¿cómo puedo crear una colección que tenga propiedades enlazables que funcione de manera similar a las propiedades del modelo?

Este es el código que me gustaría que funcione.

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); 
} 


actualización

Ver mi solución completa como una respuesta a continuación.

Respuesta

5

Así que no hay una forma 'integrada' de responder a los cambios de propiedad en una Colección, ya que realmente no hay una forma compatible (que yo sepa) de tener propiedades en una colección. Sin embargo, es totalmente posible así, creo. (No probado, pero debe ser bastante cerca)

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) 

Una nota: no desea isValid: aparece como propiedad como lo haría modelar o URL. Esto parece hacer que la red troncal sea rara, y tu valor esValid podría abarcar todas las instancias de la colección. Es mejor definirlos como un inicializador, luego, cada vez que crea una instancia de esa colección tendrá una instancia de isValid accesible por this.isValid.

+0

Gracias por la respuesta. Creo que me has señalado en la dirección correcta. Apreciar la información que es válida debe crearse en inicializar. Cuando termine con mi solución de trabajo, la publicaré. –

2

Aquí está mi solución completa a esta pregunta: jsFiddle Complete example

@spotmat me puso en la dirección correcta, pero necesitaba para añadir funcionalidad adicional. He aquí algunos artículos que deben ser resueltos:

  • Colección contructor necesita para manejar los datos pased a ella
  • Cuando un modelo de propiedad IsLocked se actualiza la colección tiene que ser re-validado

I No estoy seguro de si agregar propiedades vinculantes a una colección es una gran idea, pero fue algo divertido de entender, y aprendí mucho.

var log = {};//this is for debugging 
_.extend(log, Backbone.Events); 

var Car = Backbone.Model.extend({}); 

var Cars = Backbone.Collection.extend({ 
    model: Car, 
    url: "scripts/data/Cars.json", 
initialize: function() { 
    var _this = this; 

    this.isValid = false; //user should bind to "change:isValid" 
    this._isPending = false; //This is so that 

    this.bind("add", this.modelAdded, this); 
    this.bind("reset", this.modelsAdded, this); 

    if (this.length > 0) { 
     //Model passed in though the constructor will not be binded 
     //so call modelsAdded 
     this.modelsAdded(this); 
    } 
}, 

modelsAdded: function (collection) { 
    this._isPending = true 
    log.trigger("log", ["modelsAdded: " + collection.length]); 
    collection.each(this.modelAdded, this); //Do nut remove "this" param. It need when modelAdded gets called 
    this._isPending = false 
    this._validate(); 
}, 

modelAdded: function (model) { 
    log.trigger("log", ["modelAdded: " + model.get("Model") + "; IsLocked: " + model.get("IsLocked")]); 
    //!!!for each model added to the colleciton bind 
    //its IsLocked property to the collection modelIsLockedChange function 
    model.bind("change:IsLocked", this.modelIsLockedChanged, this); 
    if (this._isPending == false) { 
     this._validate(); 
    } 
}, 

modelIsLockedChanged: function (model) { 
    log.trigger("log", ["modelIsLockedChanged:" + model.get("Model") + "; IsLocked: " + model.get("IsLocked")]); 
    this._validate(); 
}, 

_validate: function() { 
    var isValid = true; 
    this.each(function (model) { 
     if (model.get("IsLocked") == false) { 
      isValid = false 
     } 
    }); 

    if (this.isValid != isValid) { 
     this.isValid = isValid 
     cars.trigger("change:isValid", [this.isValid]) 
    } 
    }, 
}); 

Este punto de vista es para fines de depuración solamente

var Logger = Backbone.View.extend({ 
    el: $("#output"), 

    initialize: function(){ 
     log.bind("log", this.logMessage, this) 
    }, 

    render: function(){ 
     return $(this.el).html(""); 
    }, 

    logMessage: function(message){ 
     $(this.el).find("ul").append("<li>" + message[0] + "</li>"); 
    } 
}) 

El siguiente código se utiliza para probar la colección

var logger = new Logger(); 
log.bind("log", function(message){ 
    console.log(message[0]); 
}); 

var carsData = [ 
    { "Model": "Chevy Camero", "Year": 1982, "IsLocked": true }, 
    { "Model": "Ford F-150", "Year": 2011, "IsLocked": false } 
] 

var cars = new Cars(carsData); 
cars.bind("change:isValid", function(isValid){ 
    log.trigger("log", ["change:isValid: " + isValid]); 
}); 

cars.add({ "Model": "Toyota Tacoma", "Year": 2006, "IsLocked": true }); 
cars.at(1).set({ "IsLocked": true }); 
5

Usted podría utilizar algo como esto;

Backbone.Collection.prototype.bindableProperty = function (key, val) { 
    var self = this; 
    Object.defineProperty(this, key, { 
     configurable: false, 
     get: function() { return val; }, 
     set: function (v) { 
      val = v; 
      self.trigger('change:'+key, val) 
     } 
    }); 
}; 

Haga esto para agregar la propiedad;

var Cars = Backbone.Collection.extend({ 
    model: Car, 
    url: "Cars", 
    initialize: function() { 
    this.bindableProperty('isValid', false); 
    } 
}); 

Advertencia; las versiones anteriores de IE no son compatibles con Object.defineProperty.

+0

Voy a probar esto esta noche. –

+1

Gracias Stephen Belanger por su respuesta. La solución que proporcione es probablemente la más limpia, pero como mencionó, no funciona con IE8. Aunque tuve que escribir un poco más de código para que mi ejemplo funcione con su recomendación, ya no necesito llamar manualmente al siguiente código, que es muy bueno: 'cars.trigger (" change: isValid " , [this.isValid]) ' Ahora, cada vez que se cambia la propiedad isValid, el evento se desencadena automáticamente. [jsFiddle Example] (http://jsfiddle.net/BarDev/ae63d/) –

+0

Solo para cualquiera que pueda leer esto en el futuro; SI FUNCIONA en IE9 +, y parcialmente en IE8, como se explica aquí; https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/defineProperty#ie8-specific –

Cuestiones relacionadas