2012-01-27 11 views
8

Questo è un po 'di esempio forzato, ma credo che dia il merito.Backbone.js - Come creare una collezione che ha proprietà associabili

Diciamo che ho una collezione di macchine backbone. E per la raccolta voglio una proprietà chiamata isValid. Voglio che altri oggetti siano in grado di collegarsi a isValid e attivare una funzione quando èValid cambia. La raccolta è valida La proprietà verrà modificata in base ai modelli di una raccolta nel suo complesso.

Ad esempio, se tutte le porte sono bloccate su ogni automobile, allora isValid dovrebbe passare a true. Quando sono le modifiche Valori validi, tutte le funzioni associate a Evento Evento valido devono essere attivate.

La mia domanda è, come posso creare una collezione che ha proprietà associabili che funzionano in modo simile alle proprietà del modello?

Questo è il codice che mi piacerebbe avere a lavorare.

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


Aggiornamento

Vedi il mio soluzione completa come una risposta di seguito.

risposta

5

Quindi non esiste un modo 'built-in' per rispondere alle modifiche alle proprietà su una raccolta, in quanto non esiste un modo supportato (che io sappia) per avere proprietà su una raccolta. Tuttavia, è ancora totalmente possibile come questo, immagino. (Non testato, ma dovrebbe essere abbastanza vicino)

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: non si vuole isValid: elencato come proprietà come si potrebbe modellare o un URL. Ciò sembra rendere strano il backbone e il tuo isValid potrebbe estendersi su tutte le istanze della raccolta. È meglio definirli come inizializzatore, quindi ogni volta che istanziate quella raccolta avrete un'istanza di isValid accessibile da this.isValid.

+0

Grazie per la risposta. Credo che tu mi abbia indicato nella direzione giusta. Apprezzare le informazioni che è valido Devono essere creati in fase di inizializzazione. Quando avrò finito con la mia soluzione di lavoro, la posterò. –

2

Ecco la mia soluzione completa a questa domanda: jsFiddle Complete example

@spotmat mi ha messo nella giusta direzione, ma avevo bisogno di aggiungere ulteriori funzionalità. Ecco alcuni elementi che dovevano essere risolti:

  • Collection contructor ha bisogno per gestire i dati pased ad esso
  • Quando una proprietà È bloccato modello viene aggiornata la raccolta deve essere ri-convalidato

I Non sono sicuro che aggiungere proprietà vincolanti a una collezione sia una buona idea, ma è stato qualcosa di divertente da capire e ho imparato molto.

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

Questo punto di vista è a scopo di debug solo

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

Il codice seguente viene utilizzato per testare la raccolta

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

Si potrebbe usare qualcosa di simile;

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

Basta fare questo per aggiungere la proprietà;

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

Attenzione; le versioni precedenti di IE non supportano Object.defineProperty.

+0

Ci proverò questa sera. –

+1

Grazie Stephen Belanger per la tua risposta. La soluzione che fornisci è probabilmente la più pulita, ma come hai detto non funziona con IE8. Anche se dovevo scrivere un po 'più di codice per ottenere il mio esempio per lavorare con la tua raccomandazione, non ho più bisogno di chiamare manualmente il seguente codice, che è molto bello: 'cars.trigger (" change: isValid " , [this.isValid]) ' Ora ogni volta che la proprietà isValid è cambiata, l'evento viene attivato automaticamente. [esempio jsFiddle] (http://jsfiddle.net/BarDev/ae63d/) –

+0

Solo per chiunque possa leggerlo in futuro; funziona in IE9 + e parzialmente in IE8, come spiegato qui; https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/defineProperty#ie8-specific –

Problemi correlati