web-dev-qa-db-fra.com

Backbone.js: le changement ne se déclenche pas sur model.change ()

Je fais face à un problème de "changement d'événement ne se déclenchant pas" sur Backbone.js = /

Voici ma vue du modèle utilisateur:

    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');

        }

    });

Ici, le modèle utilisateur:

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

            }
        }
    });

Dans le modèle utilisateur, il existe un attribut "cas", qui est un tableau d'objets.

J'ai lu dans d'autres sujets que les événements de changement ne se déclenchent pas sur model.set si les attributs ne sont pas une valeur.

Donc, j'essaie de déclencher directement l'événement change avec la méthode model.change (). Mais, je n'ai pas de log "foo" dans ma console ...

35
Atyz

Je suis assez nouveau pour l'épine dorsale et j'avais ce même problème.

Après avoir fait quelques recherches, j'ai trouvé quelques articles qui éclairent un peu plus sur la raison pour laquelle cela se produisait, et finalement les choses ont commencé à avoir un sens:

Question 1

Question 2

La raison principale est liée à la notion d'égalité de référence par rapport à l'égalité ensemble/membre. Il semble que, dans une large mesure, l'égalité de référence est l'une des principales techniques utilisées par le backbone pour déterminer quand un attribut a changé.

Je constate que si j'utilise des techniques qui génèrent une nouvelle référence comme Array.slice () ou _.clone (), l'événement change est reconnu.

Ainsi, par exemple, le code suivant ne déclenche pas l'événement car je modifie la même référence de tableau:

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

Bien que ce code déclenche l'événement:

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

REMARQUE: selon nderscore API , _.clone () copie certains éléments imbriqués par référence. Cependant, l'objet racine/parent est cloné, donc cela fonctionnera bien pour la dorsale. Autrement dit, si votre tableau est très simple et n'a pas de structures imbriquées, par exemple [1, 2, 3].

Alors que mon code amélioré ci-dessus a déclenché l'événement de modification, ce qui suit ne l'a pas fait car mon tableau contenait des objets imbriqués:

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

Maintenant, pourquoi est-ce important? Après un débogage très soigneux, j'ai remarqué que dans mon itérateur je faisais référence au même backbone de référence d'objet qu'il stockait. En d'autres termes, j'avais par inadvertance pénétré dans les entrailles de mon modèle et retourné un peu. Lorsque j'ai appelé setLabels (), la dorsale a correctement reconnu que rien ne changeait parce qu'elle savait déjà que j'avais inversé ce bit.

Après avoir regardé un peu plus, les gens semblent généralement dire que les opérations de copie en profondeur en javascript sont une vraie douleur - rien de intégré pour le faire. J'ai donc fait cela, ce qui a bien fonctionné pour moi - l'applicabilité générale peut varier:

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

Intéressant. J'aurais pensé que .set({cas:someArray}) aurait déclenché un événement de changement. Comme vous l'avez dit, cela ne semble pas et je ne peux pas le faire fonctionner avec .change() MAIS, je peux faire fonctionner les événements si je fais juste model.trigger('change') ou model.trigger('change:attribute')

Cela vous permettrait de déclencher l'événement de changement sans ce piratage d'attribut aléatoire.

Si quelqu'un pouvait expliquer ce qui se passe avec les événements, Backbone et ce code, cela m'aiderait à apprendre quelque chose aussi ... Voici du code.

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

La partie intéressante qui pourrait vous aider:

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

Je trouve cela intéressant et j'espère que cette réponse engendrera également des explications perspicaces dans des commentaires que je n'ai pas.

14
jmk2142