当我们设置更新的对象时,如何在Backbone模型上触发更改事件

时间:2014-04-28 09:01:38

标签: javascript backbone.js underscore.js clone

Backbone不会触发'更改'模型上的事件,当我们设置更新的对象时。看this code

var MyModel = Backbone.Model.extend({
  defaults: {
    x: {}
  }
});

var myModel = new MyModel();
myModel.on('change', function() { console.log('Changed'); });

var x = myModel.get('x');
x.something = 5; // Update the object
myModel.set('x', x); // Set the object. Does not trigger change event

解决方案:

Backbone Model已经存储了一个引用' x'对象{}。当我们更新对象并将对象再次设置到模型中时,引用仍然是相同的因此它不会触发更改事件。

其中一种方法是在设置模型之前克隆对象。看this code

var MyModel = Backbone.Model.extend({
  defaults: {
    x: {}
  }
});

var myModel = new MyModel();
myModel.on('change', function() { console.log('Changed'); });

var x2 = _.clone(myModel.get('x'));
x2.something = 6;
myModel.set('x', x2);

问:这个问题有更好的方法吗?我不确定我是否做得对。

1 个答案:

答案 0 :(得分:1)

这很棘手。由于您已经提到的原因,默认情况下您不能这样做。 您获得的对象与您稍后设置的对象相同。因此,它不仅不会检测到更改事件,还会在执行x.something = 5;时更改模型中的数据而不会意识到这一点。

情况甚至更糟:

var MyModel = Backbone.Model.extend({
  defaults: {
    x: {}
  }
});

var myModel = new MyModel();
var myModel2 = new MyModel();

var x = myModel.get('x');
x.something = 5;

console.log(myModel2.get('x'));
>> { something: 5 }

x的默认值将在所有实例之间共享,因为它是原型的一部分,被复制到实例并仍然引用原型的同一对象。 所以基本上,除了布尔值,数字和字符串之外,你不能使用默认值,并且你不能在任何其他类型上获得任何更改事件,除非你在变异或传入新的唯一对象之前克隆它们。

Backbone在其属性中的非平面数据结构中确实无法正常工作。

您的解决方案是其中一种可能性,但我可能会扩展基础Backbone.Model并将_.clone调用放入get方法。

var DeepModel = Backbone.Model.extend({
    get: function (attr) {
        return _.clone(Backbone.Model.prototype.get.apply(this, arguments));
    }
});

var myModel = new DeepModel({
    x: {
        something: 5
    }
});

myModel.on('change', console.log.bind(console, 'Changed'));
var x = myModel.get('x');
x.something = 10;
myModel.set('x', x);

然后,您还可以覆盖set方法以触发更精确的事件,以便您还可以查看确切更改的字段。 知道change:x会有点无用。

当然其他人也有同样的问题。 我知道至少有两个图书馆试图解决这类问题:

Backbone Relational
backbone-deep-model

Backbone Relational非常重,因此在模型上处理非平面属性可能不是真正的选择。 我从未使用过骨干深度模型。只需检查一下,看看它是否适合您的使用案例。 我打赌还有其他库可以解决这个问题。 或者只是想出解决这个问题的方法并开源解决方案。