Backbone.js - 如何创建具有可绑定属性的集合

时间:2012-01-27 00:16:43

标签: javascript 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);
}

<小时/> 更新

请在下面查看我的完整解决方案。

3 个答案:

答案 0 :(得分:5)

因此,没有“内置”方式来响应Collection上的属性更改,因为实际上没有支持的方式(我知道)在集合上具有属性。但是,我估计它仍然完全可能。 (未经测试,但应该相当接近)

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:列为属性,就像您建模或网址一样。这似乎使骨干很奇怪,你的isValid可能会跨越集合的所有实例。最好将它们定义为初始化程序,然后每次实例化该集合时,您都可以通过this.isValid正确地访问isValid的实例。

答案 1 :(得分: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。

答案 2 :(得分:2)

以下是我对此问题的完整解决方案: jsFiddle Complete example

@spotmat让我按正确的方向行事,但我需要添加其他功能。这里有一些需要解决的问题:

  • 集合构造函数需要处理基于它的数据
  • 更新模型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 });