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


105

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

1)以下のコードを使用して、obj1.myAttribute1を直接「取得」または「設定」するにはどうすればよいですか?

別の質問:

2)モデルでは、デフォルトオブジェクト以外に、バックボーンの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;

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


3
質問に対する回答ではありませんが、オブジェクト(defaultsこの場合はobj1 )でオブジェクト(参照によって渡されるもの)を指定すると、モデルのすべてのインスタンスで同じオブジェクトが共有されます。現在の慣行ではdefaults、デフォルトとして使用されるオブジェクトを返す関数として定義しています。backbonejs.org/#Model-defaults(斜体のメモを参照)
Jonathan F

1
@JonathanFコメントは回答用ではないため、宣言は必要ありませんでした:)
TJ

回答:


144

一方でthis.model.get("obj1").myAttribute1結構です、あなたがセットのためのものと同じ種類の操作を行うために誘惑されるかもしれないので、それはすなわち、ビットは問題です

this.model.get("obj1").myAttribute1 = true;

ただし、これを行うとmyAttribute1、変更イベントや検証など、のバックボーンモデルの利点が得られません。

より良い解決策は、モデルで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/


24
この答えに、私はこの解決策が広範囲にわたるデメテルの法則違反にぐらついているという助言を付け加えます。ネストされたオブジェクトへのナビゲーションを隠す便利なメソッドを追加することを検討します。基本的に、呼び出し元はモデルの内部構造を知っている必要はありません。結局のところ、それは変わる可能性があり、呼び出し元は賢明ではないはずです。
ビルアイゼンハウアー

7
これを機能させることができません。エラーがUncaught TypeError: Object #<Object> has no method 'set'
スローされ

1
@ ChristianNunciato、pagewil、Benno:バックボーンモデルをバックボーンモデル内にネストするという投稿の要点を逃したようです。プレーンオブジェクトをバックボーンモデル内にネストしないでください。ここでの作業例:jsfiddle.net/g3U7j
Domenic

1
私はbackbone.jsコードを検査しませんでしたが、私のテストから、ネストされたカスタムモデルがあり、そのプロパティをset()で変更した場合、その親モデル自体は「変更」イベントを発生させません。私は自分でイベントを発生させなければなりませんでした。私は本当にコードを検査する必要がありますが、これもあなたの理解ですか?
tom

2
正しい@tomです。バックボーンは、モデルのプロパティがのインスタンスである場合の特別なケースではなくBackbone.Model、魔法のイベントバブリングを開始します。
Domenic 2013年

74

このために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

1
はい、それはあなたが見れば、ないAPIのような例がある//You can use index notation to fetch from arrays console.log(model.get('otherSpies.0.name')) //'Lana'
タウヒード

よく働く!しかし、あなたの例の2行目には、コンマの代わりにコロンが必要ですか?
マリアッチ2014

16

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

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

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


1
今後の読者のために、私の回答は、Rustyの回答のベストを組み込むように更新されました。
Domenic '15

2
アスカーはこれを受け入れられた回答としてフラグを立てる必要があります。Domenicは素晴らしいスタートですが、これで問題は解決しました。
Jon Raasch

5

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

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

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

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


1
あなたはこれについて確信を持っていますか?これは私にはうまくいきません。meta2= m.get('x'); meta2.id=110; m.set('x', meta2)。これは私のための変更イベントをトリガーしません:(
HungryCoder 2013年

1
のように属性を複製すると機能することがわかります_.clone(m.get('x'))。感謝
HungryCoder 2013年

@HungryCoderに感謝します。バックボーンはsetting、現在のオブジェクトとgetting設定された時間のオブジェクトを比較する必要があります。したがって、2つのオブジェクトを複製しない場合、比較される2つのオブジェクトは、設定された時間でまったく同じになります。
Derek Dahmer、2013年

文字列や数値のプリミティブとは異なり、オブジェクトは参照によって渡され、変更可能であることを覚えておいてください。バックボーンのセットおよびコンストラクターメソッドは、引数として渡されたオブジェクト参照の浅い複製を試みます。そのオブジェクトのプロパティにある他のオブジェクトへの参照は複製されません。設定して取得しても参照は同じです。つまり、変更をトリガーせずにモデルを変更できます。
niall.campbell

3

まだ誰も考えていない解決策が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

2

@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

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

0

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

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

ネスト構造を持つオブジェクトが必要なバックエンドとやり取りする場合。ただし、バックボーンを使用すると、線形構造で作業しやすくなります。

backbone.linearが役立ちます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.