ChatGPT解决这个技术问题 Extra ChatGPT

Backbone.js 获取和设置嵌套对象属性

我有一个关于 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(见斜体注释)
@JonathanF 评论并不意味着答案,所以你永远不需要声明:)

D
Domenic

虽然 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/


对于这个答案,我要补充一点建议,即这个解决方案在广泛违反德米特法则的情况下摇摇欲坠。我会考虑添加隐藏导航到嵌套对象的便捷方法。基本上,你的调用者不需要知道模型的内部结构;毕竟,它可能会改变,调用者不应该更聪明。
不能让这个为我工作。引发错误:Uncaught TypeError: Object #<Object> has no method 'set'
@ChristianNunciato,pagewil,Benno:您似乎错过了帖子的重点,即将 Backbone 模型嵌套在 Backbone 模型中。不要在 Backbone 模型中嵌套普通对象。此处的工作示例:jsfiddle.net/g3U7j
我没有检查backbone.js 代码,但是根据我的测试,如果您有一个嵌套的自定义模型并使用set() 更改它的属性,那么它的父模型本身不会触发“更改”事件;我不得不自己开火。我真的应该只检查代码,但这也是你的理解吗?
@tom 那是正确的。当模型的属性是 Backbone.Model 的实例时,Backbone 没有特殊情况,然后开始进行神奇的事件冒泡。
e
evilcelery

我为此创建了 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

是的,如果您查看 API,就会看到类似 //You can use index notation to fetch from arrays console.log(model.get('otherSpies.0.name')) //'Lana' 的示例
效果很好!但是您示例中的第 2 行是否需要冒号而不是逗号?
C
Community

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


对于未来的读者,我的答案已经更新,以纳入 Rusty 的最佳答案。
提问者应将此标记为已接受的答案。 Domenic 是一个很好的开始,但这解决了它的一个问题。
N
Natthakit Susanthitanon

我使用这种方法。

如果您有这样的 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')) 之类的属性时,我发现它有效。谢谢
感谢@HungryCoder,它在克隆时也对我有用。 Backbone 必须在设定的时间将您是 setting 的对象与您是 getting 的对象进行比较。因此,如果您不克隆两个对象,那么被比较的两个对象在设定的时间完全相同。
请记住,对象通过引用传递并且是可变的,这与字符串和数字原语不同。 Backbone 的 set 和 constructor 方法尝试对作为参数传递的任何对象引用进行浅层克隆。该对象的属性中对其他对象的任何引用都不会被克隆。当您设置它并检索它时,引用是相同的,这意味着您可以在不触发更改的情况下改变模型。
b
bicycle

有一种没有人想到的解决方案可以使用很多。您确实不能直接设置嵌套属性,除非您使用您可能不想要的第三方库。但是,您可以做的是克隆原始字典,在那里设置嵌套属性,然后设置整个字典。小菜一碟。

//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

C
Cory Gagliardi

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

在初始化时自动生成模型列表很容易,但这个解决方案对我来说已经足够好了。


y
yuliskov

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

I
Ibrahim Muhammad

虽然在某些情况下使用 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);
    }
})

d
darky

如果您与后端交互,则需要具有嵌套结构的对象。但是骨干更容易使用线性结构。

backbone.linear 可以帮助您。