我有一个关于 Backbone.js 的 get 和 set 函数的简单问题。
1) 使用下面的代码,如何直接“获取”或“设置”obj1.myAttribute1?
另一个问题:
2) 在模型中,除了默认对象之外,我可以/应该在哪里声明模型的其他属性,以便可以通过 Backbone 的 get 和 set 方法访问它们?
var MyModel = Backbone.Model.extend({
defaults: {
obj1 : {
"myAttribute1" : false,
"myAttribute2" : true,
}
}
})
var MyView = Backbone.View.extend({
myFunc: function(){
console.log(this.model.get("obj1"));
//returns the obj1 object
//but how do I get obj1.myAttribute1 directly so that it returns false?
}
});
我知道我可以做到:
this.model.get("obj1").myAttribute1;
但这是好的做法吗?
defaults
(在本例中为 obj1)中指定一个对象(通过引用传递的任何内容)时,该 same 对象将在模型。当前的做法是将 defaults
定义为返回要用作默认值的对象的函数。 backbonejs.org/#Model-defaults(见斜体注释)
虽然 this.model.get("obj1").myAttribute1
很好,但它有点问题,因为你可能会想为 set 做同样类型的事情,即
this.model.get("obj1").myAttribute1 = true;
但是,如果您这样做,您将无法获得 myAttribute1
的 Backbone 模型的好处,例如更改事件或验证。
更好的解决方案是永远不要在模型中嵌套 POJSO(“普通旧 JavaScript 对象”),而是嵌套自定义模型类。所以它看起来像这样:
var Obj = Backbone.Model.extend({
defaults: {
myAttribute1: false,
myAttribute2: true
}
});
var MyModel = Backbone.Model.extend({
initialize: function () {
this.set("obj1", new Obj());
}
});
然后访问代码将是
var x = this.model.get("obj1").get("myAttribute1");
但更重要的是设置代码是
this.model.get("obj1").set({ myAttribute1: true });
这将触发适当的更改事件等。此处的工作示例:http://jsfiddle.net/g3U7j/
我为此创建了 backbone-deep-model - 只需扩展 Backbone.DeepModel 而不是 Backbone.Model,然后您就可以使用路径来获取/设置嵌套模型属性。它也维护更改事件。
model.bind('change:user.name.first', function(){...});
model.set({'user.name.first': 'Eric'});
model.get('user.name.first'); //Eric
//You can use index notation to fetch from arrays console.log(model.get('otherSpies.0.name')) //'Lana'
的示例
Domenic 的解决方案可以工作,但是每个新的 MyModel 都将指向同一个 Obj 实例。为避免这种情况,MyModel 应如下所示:
var MyModel = Backbone.Model.extend({
initialize: function() {
myDefaults = {
obj1: new Obj()
}
this.set(myDefaults);
}
});
有关完整说明,请参阅 c3rin 的回答 @https://stackoverflow.com/a/6364480/1072653。
我使用这种方法。
如果您有这样的 Backbone 模型:
var nestedAttrModel = new Backbone.Model({
a: {b: 1, c: 2}
});
您可以使用以下方式设置属性“ab”:
var _a = _.omit(nestedAttrModel.get('a')); // from underscore.js
_a.b = 3;
nestedAttrModel.set('a', _a);
现在您的模型将具有以下属性:
{a: {b: 3, c: 2}}
触发“更改”事件。
meta2= m.get('x'); meta2.id=110; m.set('x', meta2)
。这不会为我触发任何更改事件:(
_.clone(m.get('x'))
之类的属性时,我发现它有效。谢谢
setting
的对象与您是 getting
的对象进行比较。因此,如果您不克隆两个对象,那么被比较的两个对象在设定的时间完全相同。
有一种没有人想到的解决方案可以使用很多。您确实不能直接设置嵌套属性,除非您使用您可能不想要的第三方库。但是,您可以做的是克隆原始字典,在那里设置嵌套属性,然后设置整个字典。小菜一碟。
//How model.obj1 looks like
obj1: {
myAttribute1: false,
myAttribute2: true,
anotherNestedDict: {
myAttribute3: false
}
}
//Make a clone of it
var cloneOfObject1 = JSON.parse(JSON.stringify(this.model.get('obj1')));
//Let's day we want to change myAttribute1 to false and myAttribute3 to true
cloneOfObject1.myAttribute2 = false;
cloneOfObject1.anotherNestedDict.myAttribute3 = true;
//And now we set the whole dictionary
this.model.set('obj1', cloneOfObject1);
//Job done, happy birthday
@pagewil 和@Benno 对@Domenic 的解决方案有同样的问题。我的答案是编写一个简单的 Backbone.Model 子类来解决问题。
// Special model implementation that allows you to easily nest Backbone models as properties.
Backbone.NestedModel = Backbone.Model.extend({
// Define Backbone models that are present in properties
// Expected Format:
// [{key: 'courses', model: Course}]
models: [],
set: function(key, value, options) {
var attrs, attr, val;
if (_.isObject(key) || key == null) {
attrs = key;
options = value;
} else {
attrs = {};
attrs[key] = value;
}
_.each(this.models, function(item){
if (_.isObject(attrs[item.key])) {
attrs[item.key] = new item.model(attrs[item.key]);
}
},this);
return Backbone.Model.prototype.set.call(this, attrs, options);
}
});
var Obj = Backbone.Model.extend({
defaults: {
myAttribute1: false,
myAttribute2: true
}
});
var MyModel = Backbone.NestedModel.extend({
defaults: {
obj1: new Obj()
},
models: [{key: 'obj1', model: Obj}]
});
NestedModel 为您做的是允许这些工作(当 myModel 通过 JSON 数据设置时会发生这种情况):
var myModel = new MyModel();
myModel.set({ obj1: { myAttribute1: 'abc', myAttribute2: 'xyz' } });
myModel.set('obj1', { myAttribute1: 123, myAttribute2: 456 });
在初始化时自动生成模型列表很容易,但这个解决方案对我来说已经足够好了。
Domenic 提出的解决方案有一些缺点。假设您想收听“更改”事件。在这种情况下,不会触发“初始化”方法,并且您的属性自定义值将替换为来自服务器的 json 对象。在我的项目中,我遇到了这个问题。我覆盖模型的“设置”方法的解决方案:
set: function(key, val, options) {
if (typeof key === 'object') {
var attrs = key;
attrs.content = new module.BaseItem(attrs.content || {});
attrs.children = new module.MenuItems(attrs.children || []);
}
return Backbone.Model.prototype.set.call(this, key, val, options);
},
虽然在某些情况下使用 Backbone 模型而不是嵌套的 Object 属性是有意义的,正如 Domenic 所提到的,但在更简单的情况下,您可以在模型中创建一个 setter 函数:
var MyModel = Backbone.Model.extend({
defaults: {
obj1 : {
"myAttribute1" : false,
"myAttribute2" : true,
}
},
setObj1Attribute: function(name, value) {
var obj1 = this.get('obj1');
obj1[name] = value;
this.set('obj1', obj1);
}
})
Uncaught TypeError: Object #<Object> has no method 'set'
Backbone.Model
的实例时,Backbone 没有特殊情况,然后开始进行神奇的事件冒泡。