web-dev-qa-db-ja.com

Backbone.jsはネストされたオブジェクト属性を取得および設定します

Backbone.jsのgetおよびset関数について簡単な質問があります。

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;

しかし、それは良い習慣ですか?

105
fortuneRice

this.model.get("obj1").myAttribute1は問題ありませんが、セットに対して同じタイプのことをしようとする可能性があるため、少し問題があります。

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/

144
Domenic

このために backbone-deep-model を作成しました-Backbone.ModelではなくBackbone.DeepModelを拡張するだけで、パスを使用してネストされたモデル属性を取得/設定できます。変更イベントも保持します。

model.bind('change:user.name.first', function(){...});
model.set({'user.name.first': 'Eric'});
model.get('user.name.first'); //Eric
74
evilcelery

Domenicのソリューションは機能しますが、新しいMyModelはそれぞれObjの同じインスタンスを指します。これを回避するには、MyModelは次のようになります。

var MyModel = Backbone.Model.extend({
  initialize: function() {
     myDefaults = {
       obj1: new Obj()
     } 
     this.set(myDefaults);
  }
});

完全な説明については、c3rinの回答@ https://stackoverflow.com/a/6364480/107265 を参照してください。

16
Rusty

私はこのアプローチを使用します。

このようなバックボーンモデルがある場合:

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

「変更」イベントが発生します。

まだ誰も考えていない解決策が1つあります。おそらく望まないサードパーティのライブラリを使用しない限り、ネストされた属性を直接設定することはできません。ただし、元の辞書のクローンを作成し、そこにネストされたプロパティを設定してから、その辞書全体を設定することができます。ケーキ。

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

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

初期化でモデルリストを自動的に生成するのは簡単ですが、このソリューションは私にとっては十分でした。

2
Cory Gagliardi

Domenicによって提案されたソリューションには、いくつかの欠点があります。 「変更」イベントを聞きたいとします。その場合、「initialize」メソッドは起動されず、属性のカスタム値はサーバーからのjsonオブジェクトに置き換えられます。私のプロジェクトでは、この問題に直面しました。モデルの「set」メソッドをオーバーライドする私のソリューション:

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

バックエンドと対話する場合、ネスト構造を持つオブジェクトが必要です。しかし、バックボーンを使用すると、線形構造での作業がより簡単になります。

backbone.linear が役立ちます。

0
darky

Domenicが述べたように、ネストされたObject属性の代わりにBackboneモデルを使用することは理にかなっていますが、より単純な場合、モデルにセッター関数を作成できます。

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