如何在骨干模型中处理“更改”事件?

时间:2012-11-13 18:09:30

标签: backbone.js model meteor

我正在尝试构建一个动态更新Meteor项目中的Session变量的模型。我知道普通的JSON不应该存储在骨干模型中,所以我有一个特殊的模型设置如下:

initialize : function () {
    // Log the changed properties
    this.on('change', function (model, options) {
        for ( var i in options.changes)
            this.display(i);
        Session.set('NewSpecial', model);
    });
},
//Attributes
defaults: {
    "Product" : null,
    "ShortDescription" : null,
    "Category" : "food",
    "Price" : new PriceModel,
    "Date" : new DateModel,
    "Uses" : 0,
    "Tags" : [],
    "Contributor" : null
},

“价格”和“日期”存储在他们自己的模型中:

//Price model for use within Special
var PriceModel = Backbone.Model.extend({
    defaults : {
        "Regular" : null,
        "Special" : null,
        "PercentOff" : null
    }
});

//Date model for use within Special
var DateModel = Backbone.Model.extend({
    defaults : {
        "StartTime" : null,
        "EndTime" : null,
        "HumanTimeRange" : null
    }
});

如图所示,当Special模型的属性发生更改时,应调用显示的更改属性,然后将Session var设置为模型。但是,如果我的DateModel或PriceModel发生更改,则它似乎不会触发Special模型上的更改事件。每个“DateModel”和“PriceModel”都应该有自己的this.on('change', ...)方法来调用Special.set(attribute, thisModel)方法吗?或者有不同的方法可以解决这个问题吗?

1 个答案:

答案 0 :(得分:2)

我看到了几个问题。

首先,您的defaults

defaults: {
    "Product" : null,
    "ShortDescription" : null,
    "Category" : "food",
    "Price" : new PriceModel,
    "Date" : new DateModel,
    "Uses" : 0,
    "Tags" : [],
    "Contributor" : null
}

最终会有一个PriceModel,一个DateModel,并且该模型的所有实例都会共享一个标记数组。一个defaults对象被浅层复制并合并到模型的属性中,defaults中没有任何值被克隆或复制,它们只是按原样复制。如果您想要区分PriceDateTags值,请使用defaults的函数:

defaults: function() {
    return {
        "Product" : null,
        "ShortDescription" : null,
        "Category" : "food",
        "Price" : new PriceModel,
        "Date" : new DateModel,
        "Uses" : 0,
        "Tags" : [],
        "Contributor" : null
    };
}

第二个问题是set更改的含义有一个相当简单的看法。如果你看一下source for set,你会看到:

// If the new and previous value differ, record the change.  If not,
// then remove changes for this attribute.
if (!_.isEqual(prev[attr], val) || (_.has(now, attr) !== _.has(prev, attr))) {
  this.changed[attr] = val;
  if (!silent) this._pending[attr] = true;
} else {
  delete this.changed[attr];
  delete this._pending[attr];
  if (!changing) delete this._changes[attr];
}

_.isEqual无法识别您PriceDate内的某些内容发生了变化,或者您已从Tags添加或删除了某些内容。如果你做这样的事情:

p = new PriceModel(...);
m.set('Price', p)

然后m会注意到Price已更改,但如果您:

p = m.get('Price');
p.set(...);
m.set('Price', p);

然后m无法识别Price已发生变化;您的模型无法自动绑定到Price上的活动,因此它不会注意到p.set(...)来电,因此无法识别m.set('Price', p)作为更改#39;仅仅是一种说p = p的奇特方式。

您可以通过不set来自Tags的{​​{1}}数组来解决部分此更改问题;制作副本,更改副本,然后将更新的副本交给get。可以通过绑定到包含的set"change"模型上的Price事件来处理这一半,并将其转发类似于集合的执行方式,如下所示:

Date

您希望提供自己的initialize: function() { this.attributes.Price.on( 'all', function(ev, model, opts) { this.trigger(ev, model, opts) }, this ); //... } 实施,以防某人执行set并且您需要重新绑定转发器。