2012-01-27 26 views
8

這是一個有些人爲的例子,但我相信它會得到重點。Backbone.js - 如何創建一個具有可綁定屬性的集合

可以說我有一個骨幹汽車集合。而對於集合,我想要一個名爲isValid的屬性。我希望其他對象能夠綁定到isValid並在isValid更改時觸發一個函數。集合isValid屬性將根據集合中的模型整體進行更改。

例如,如果所有車門都鎖定在每輛車上,則isValid應更改爲true。當isValid改變時,所有綁定到isValid change事件的函數都應該被觸發。

我的問題是,我該如何創建一個具有類似於模型屬性的可綁定屬性的集合?

這是我想要工作的代碼。

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


更新

看到我的完整的解決方案如下回答。

回答

5

因此,沒有「內置」方式來響應集合上的屬性更改,因爲實際上沒有受支持的方式(我知道)在集合上具有屬性。但是,我認爲它還是完全可能的。 (未經測試,但應該是相當接近)

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) 

一記:你不希望的isValid:列爲財產像你這樣的模型或URL。這似乎使骨幹變得怪異,你的isValid可能會跨越集合的所有實例。最好將它們定義爲初始化程序,然後每次實例化該集合時,都將正確地擁有可由this.isValid訪問的isValid實例。

+0

感謝您的回答。我相信你已經指出了我的正確方向。欣賞在初始化時應該創建的有效信息。當我完成我的工作解決方案後,我會發布它。 – 2012-01-27 04:36:11

2

這裏是我的完整解決這個問題: jsFiddle Complete example

@spotmat把我在正確的方向,但我需要添加額外的功能。下面是一個需要解決的幾個項目:

  • 集合構造器需要處理pased給它的數據需要
  • 當一個模型IsLocked屬性更新的集合進行重新驗證

我'不知道是否將綁定屬性添加到集合是一個好主意,但是找到一些有趣的東西,我學到了很多東西。

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

這種觀點是僅用於調試

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

下面的代碼是用來測試集合

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

你可以使用這樣的事情;

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

只要做到這一點,添加屬性;

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

儘管警告;舊版本的IE不支持Object.defineProperty。

+0

今天晚上我會試試這個。 – 2012-01-27 15:26:05

+1

謝謝Stephen Belanger爲你解答。您提供的解決方案可能是最乾淨的,但正如您所提到的,它不適用於IE8。 即使我不得不再寫一點代碼才能讓我的示例與您的建議一起工作,我不再需要手動調用以下代碼,這是非常好的: 'cars.trigger(「change:isValid」 ,[this.isValid])' 現在無論何時更改i​​sValid屬性,事件都會自動觸發。 [jsFiddle Example](http://jsfiddle.net/BarDev/ae63d/) – 2012-01-27 17:37:07

+0

僅供任何人在未來可能會閱讀此內容;它在IE9 +中工作,部分在IE8中工作,如此處所述; https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/defineProperty#ie8-specific – 2012-02-17 21:01:55

相關問題