主干模型中如何处理“更改"事件? [英] How are 'change' events handled in backbone models?

查看:19
本文介绍了主干模型中如何处理“更改"事件?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试构建一个模型来动态更新 Meteor 项目中的会话变量.我知道普通 JSON 不应该存储在主干模型中,所以我有一个像这样设置的特殊模型:

初始化:函数(){//记录改变的属性this.on('change', function (model, options) {for ( var i in options.changes)this.display(i);Session.set('NewSpecial', 模型);});},//属性默认值:{产品":空,"ShortDescription" : null,"类别" : "食物",价格":新的价格模型,日期":新的日期模型,用途":0,标签":[],贡献者":空},

价格"和日期"存储在它们自己的模型中:

//Special 内使用的价格模型var PriceModel = Backbone.Model.extend({默认值:{常规":空,特殊":空,百分比关闭":空}});//用于特殊的日期模型var DateModel = Backbone.Model.extend({默认值:{开始时间":空,结束时间":空,人类时间范围":空}});

如图所示,当Special模型的属性发生变化时,应该为发生变化的属性调用display,然后将Session var设置为模型.但是,如果我的 DateModel 或 PriceModel 发生变化,它似乎不会在特殊模型上触发更改事件.每个DateModel"和PriceModel"是否应该有自己的this.on('change', ...) 方法来调用Special.set(attribute, thisModel) 方法?或者有什么不同的方法来解决这个问题?

解决方案

我发现了一些问题.

首先,你的defaults:

默认值:{产品":空,"ShortDescription" : null,"类别" : "食物",价格":新的价格模型,日期":新的日期模型,用途":0,标签":[],贡献者":空}

这将最终得到一个 PriceModel、一个 DateModel 和一个由该模型的所有实例共享的标签数组.defaults 对象被浅复制并合并到模型的属性中,没有defaults 中的值被克隆或复制,它们只是按原样复制.如果您想区分 PriceDateTags 值,请使用 defaults 函数:

默认值:function() {返回 {产品":空,"ShortDescription" : null,"类别" : "食物",价格":新的价格模型,日期":新的日期模型,用途":0,标签":[],贡献者":空};}

第二个问题是 set改变是什么意思.如果您查看 set 的来源,你会看到:

//如果新的和以前的值不同,记录变化.如果不,//然后删除此属性的更改.if (!_.isEqual(prev[attr], val) || (_.has(now, attr) !== _.has(prev, attr))) {this.changed[attr] = val;if (!silent) this._pending[attr] = true;} 别的 {删除 this.changed[attr];删除 this._pending[attr];if (!changed) 删除 this._changes[attr];}

_.isEqual 不会识别出你的PriceDate 或者您已从 Tags 添加或删除某些内容.如果你做这样的事情:

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

然后 m 会注意到 Price 已经改变,但是如果你:

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

然后 m 不会识别 Price 已经改变;您的模型不会自动绑定到 Price 上的事件,因此它不会注意到 p.set(...) 调用,也不会识别 m.set('Price', p) 作为一种变化,因为这只不过是一种说 p = p 的奇特方式.

你可以通过不给 set 一个来自 getTags 数组来解决这个 change 问题的一部分;制作副本,更改副本,然后将更新的副本交给set.一半可以通过绑定到包含的 PriceDate 模型上的 "change" 事件并转发它们来处理,类似于集合的方式,像这样:

初始化:函数(){this.attributes.Price.on('全部',功能(EV,模型,选择){ this.trigger(EV,模型,选择)},这);//...}

如果有人执行了 set('Price', some_new_object),您需要提供自己的 set 实现并且您需要重新绑定您的转发器.>

I'm trying to build a model that dynamically updates Session variables in a Meteor project. I know that plain JSON should not be stored within backbone models, so I have a Special model set up like so:

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
},

With "Price" and "Date" being stored in their own models:

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

As shown, when the attributes of the Special model change, it should call display for the attribute that changed, and then set the Session var to the model. If my DateModel or PriceModel change however, it doesn't appear to trigger a change event on the Special model. Should each "DateModel" and "PriceModel" have their own this.on('change', ...) methods that call Special.set(attribute, thisModel) methods? Or is there a different way to go about this?

解决方案

I see a couple problems.

First of all, your defaults:

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

That will end up with one PriceModel, one DateModel, and one tags array being shared by all instances of that model. A defaults object is shallow copied and merged into the model's attributes, none of the values in defaults are cloned or duplicated, they're just copied over as-is. If you want distinced Price, Date, and Tags values then use a function for defaults:

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

The second problem is that set has a fairly simplistic view of what change means. If you have a look at the source for set, you'll see this:

// 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];
}

The _.isEqual won't recognize that something has changed inside your Price or Date or that you've added or removed something from Tags. If you do things like this:

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

then m will noticed that Price has changed but if you:

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

then m won't recognize that Price has changed; your model won't automatically bind to events on Price so it won't notice the p.set(...) call and it won't recognize m.set('Price', p) as a change since that's little more than a fancy way of saying p = p.

You can solve part of this change problem by not giving set a Tags array that came from get; make a copy, change the copy, and then hand the updated copy to set. The half can be handled by binding to "change" events on the contained Price and Date models and forwarding them similar to how collections do it, something like this:

initialize: function() {
    this.attributes.Price.on(
        'all',
        function(ev, model, opts) { this.trigger(ev, model, opts) },
        this
    );
    //...
}

You'd want to provide your own set implementation in case someone did a set('Price', some_new_object) and you need to rebind your forwarder.

这篇关于主干模型中如何处理“更改"事件?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆