web-dev-qa-db-ja.com

Backbone.js:model.change()で起動しない変更

Backbone.jsで「変更イベントが発生しない」問題に直面しています= /

ここでユーザーモデルの私の見解:

    window.UserView = Backbone.View.extend({

        ...

        initialize: function()
        {
            this.model.on('destroy', this.remove, this);

            this.model.on('change', function()
            {
               console.log('foo');
            });
        },

        render: function(selected)
        {
            var view = this.template(this.model.toJSON());

            $(this.el).html(view);

            return this;
        },

        transfer: function(e)
        {                
            var cas = listofcas;

            var transferTo = Users.getByCid('c1');
            var transferToCas = transferTo.get('cas');

            this.model.set('cas', cas);
            console.log('current model');
            console.log(this.model);

            //this.model.change();
            this.model.trigger("change:cas");
            console.log('trigger change');

            transferTo.set('cas', transferToCas);
            console.log('transferto model');
            console.log(transferTo);

            //transferTo.change();
            transferTo.trigger("change:cas");
            console.log('trigger change');

        }

    });

ここで、ユーザーモデル:

window.User = Backbone.Model.extend({

        urlRoot: $('#pilote-manager-app').attr('data-src'),

        initialize: function()
        {
            this.set('Rand', 1);
            this.set('specialite', this.get('sfGuardUser').specialite);
            this.set('name', this.get('sfGuardUser').first_name + ' ' + this.get('sfGuardUser').last_name);
            this.set('userid', this.get('sfGuardUser').id);
            this.set('avatarsrc', this.get('sfGuardUser').avatarsrc);
            this.set('cas', new Array());

            if (undefined != this.get('sfGuardUser').SignalisationBouclePorteur) {

                var cas = new Array();

                _.each(this.get('sfGuardUser').SignalisationBouclePorteur, function(value)
                {
                    cas.Push(value.Signalisation);
                });

                this.set('cas', cas);

            }
        }
    });

ユーザーモデルには、オブジェクトの配列である「cas」属性があります。

属性が値でない場合、model.setでイベントが発生しないという変更を他のトピックで読みました。

そのため、model.change()メソッドを使用してchangeイベントを直接トリガーしようとします。しかし、コンソールに「foo」ログがありません...

35
Atyz

私はバックボーンが初めてで、これと同じ問題を抱えていました。

いくつかの研究を行った後、私はこれがなぜ起こったのかについてもう少し光を当てるいくつかの投稿を見つけました、そして最終的に物事は意味を持ち始めました:

質問1

質問2

核となる理由は、参照の等価性と集合/メンバーの等価性の概念に関係しています。参照の平等は、属性がいつ変更されたかを把握するためにバックボーンが使用する主要な手法の1つであると思われます。

Array.slice()や_.clone()などの新しい参照を生成する手法を使用すると、変更イベントが認識されます。

たとえば、同じ配列参照を変更しているため、次のコードはイベントをトリガーしません。

this.collection.each(function (caseFileModel) {
    var labelArray = caseFileModel.get("labels");
    labelArray.Push({ Key: 1, DisplayValue: messageData });
    caseFileModel.set({ "labels": labelArray });
});

このコードはイベントをトリガーしますが:

this.collection.each(function (caseFileModel) {
    var labelArray = _.clone(caseFileModel.get("labels")); // The clone() call ensures we get a new array reference - a requirement for the change event
    labelArray.Push({ Key: 1, DisplayValue: messageData });
    caseFileModel.set({ "labels": labelArray });
});

注: nderscore API によれば、_。clone()は特定のネストされたアイテムを参照によってコピーします。ただし、ルート/親オブジェクトはクローン化されているため、バックボーンでは正常に機能します。つまり、配列が非常に単純で、ネストされた構造を持たない場合、たとえば[1、2、3]。

上記の改善されたコードは変更イベントをトリガーしましたが、配列にはネストされたオブジェクトが含まれていたため、以下は発生しませんでした。

var labelArray = _.clone(this.model.get("labels"));
_.each(labelArray, function (label) {
    label.isSelected = (_.isEqual(label, selectedLabel));
});
this.model.set({ "labels": labelArray });

なぜこれが重要なのでしょうか?非常に慎重にデバッグした後、イテレータで、同じオブジェクト参照バックボーンが格納しているのを参照していることに気付きました。言い換えれば、私は意図せずに私のモデルの内部に手を伸ばし、少しひっくり返していました。 setLabels()を呼び出したとき、バックボーンは、すでに変更されていないため、何も変更されていないことを正しく認識しましたknewそのビットを反転しました。

もう少し調べてみると、一般的に、javascriptでのディープコピー操作は本当に苦痛だと言われているようです。だから私はこれをやった、それは私のためにうまくいった-一般的な適用可能性は異なる場合があります:

var labelArray = JSON.parse(JSON.stringify(this.model.get("labels")));
_.each(labelArray, function (label) {
    label.isSelected = (_.isEqual(label, selectedLabel));
});
this.model.set({ "labels": labelArray });
70
killthrush

面白い。 .set({cas:someArray})が変更イベントを開始すると思っていただろう。あなたが言ったように、それはそうではないようで、.change()で起動させることはできませんが、model.trigger('change')またはmodel.trigger('change:attribute')

これにより、ランダムな属性ハッキングなしで変更イベントをトリガーできます。

誰かがイベント、Backbone、およびこのコードで何が起こっているのかを説明できれば、それは私も何かを学ぶのに役立ちます...ここにいくつかのコードがあります。

Ship = Backbone.Model.extend({
    defaults: {
        name:'titanic',
        cas: new Array()
    },
    initialize: function() {
        this.on('change:cas', this.notify, this);
        this.on('change', this.notifyGeneral, this);
    },
    notify: function() {
        console.log('cas changed');
    },
    notifyGeneral: function() {
        console.log('general change');
    }
});

myShip = new Ship();

myShip.set('cas',new Array());
    // No event fired off

myShip.set({cas: [1,2,3]});  // <- Why? Compared to next "Why?", why does this work?
    // cas changed
    // general change

myArray = new Array();
myArray.Push(4,5,6);

myShip.set({cas:myArray});  // <- Why?
    // No event fired off
myShip.toJSON();
    // Array[3] is definitely there

myShip.change();
    // No event fired off

あなたを助けるかもしれない興味深い部分:

myShip.trigger('change');
    // general change
myShip.trigger('change:cas');
    // cas changed

私はこれが面白いと思うし、この答えが私が持っていないコメントで洞察に満ちた説明を生むことを願っています。

14
jmk2142