【问题标题】:Backbone.js get and set nested object attributeBackbone.js 获取和设置嵌套对象属性
【发布时间】:2011-06-14 23:13:32
【问题描述】:

我有一个关于 Backbone.js 的 getset 函数的简单问题。

1) 使用下面的代码,如何直接“获取”或“设置”obj1.myAttribute1?

另一个问题:

2) 在模型中,除了 defaults 对象之外,我可以/应该在哪里声明模型的其他属性,以便可以通过 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 评论不适用于 answers,因此您从不需要声明 :)

标签: javascript backbone.js backbone-model


【解决方案1】:

虽然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 没有特殊情况,然后开始进行神奇的事件冒泡。
【解决方案2】:

我为此创建了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 行是否需要冒号而不是逗号?
【解决方案3】:

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

【讨论】:

    【解决方案4】:

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

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

    【讨论】:

      【解决方案5】:

      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 是一个很好的开始,但这解决了它的一个问题。
      【解决方案6】:

      我使用这种方法。

      如果您有这样的 Backbone 模型:

      var nestedAttrModel = new Backbone.Model({
          a: {b: 1, c: 2}
      });
      

      您可以使用以下方式设置属性“a.b”:

      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 方法尝试对作为参数传递的任何对象引用进行浅层克隆。该对象的属性中对其他对象的任何引用都不会被克隆。当您设置它并检索它时,引用是相同的,这意味着您可以在不触发更改的情况下改变模型。
      【解决方案7】:

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

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

      【讨论】:

        【解决方案8】:

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

        【讨论】:

          【解决方案9】:

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

          backbone.linear可以帮到你。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2010-11-05
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-09-17
            • 2013-09-27
            相关资源
            最近更新 更多