38 votes

Backbone : événement perdu lors du re-rendu

J'ai super-vue qui est chargé de rendre sous-vues . Quand je re-rendu el super-vue tous les événements de la sous-vues sont perdus.

Voici un exemple :

var SubView = Backbone.View.extend({
    events: {
        "click": "click"
    },

    click: function(){
        console.log( "click!" );
    },

    render: function(){
        this.$el.html( "click me" );
        return this;
    }
});

var Composer = Backbone.View.extend({
    initialize: function(){
        this.subView = new SubView();
    },

    render: function(){
        this.$el.html( this.subView.render().el );             
    }
});

var composer = new Composer({el: $('#composer')});
composer.render();

Lorsque je clique dans le cliquez sur moi div l'événement est déclenché. Si j'exécute composer.render() Encore une fois, tout semble identique, mais le événement de clic n'est plus déclenché.

Vérifiez le jsFiddle de travail .

66voto

mu is too short Points 205090

Quand tu fais ça :

this.$el.html( this.subView.render().el );

Vous dites en fait ceci :

this.$el.empty();
this.$el.append( this.subView.render().el );

et empty tue les événements sur tout ce qui est à l'intérieur this.$el :

Pour éviter les fuites de mémoire, jQuery supprime les autres constructions, telles que les données et les gestionnaires d'événements, des éléments enfants avant de supprimer les éléments eux-mêmes.

Donc vous perdez le delegate qui lie les événements sur this.subView y el SubView#render ne les reliera pas.

Vous devez glisser un this.subView.delegateEvents() l'appel en this.$el.html() mais vous avez besoin que cela se produise après le empty() . Vous pourriez le faire comme ça :

render: function(){
    console.log( "Composer.render" );
    this.$el.empty();
    this.subView.delegateEvents();
    this.$el.append( this.subView.render().el );             
    return this;
}

Démonstration : http://jsfiddle.net/ambiguous/57maA/1/

Ou comme ça :

render: function(){
    console.log( "Composer.render" );
    this.$el.html( this.subView.render().el );             
    this.subView.delegateEvents();
    return this;
}

Démonstration : http://jsfiddle.net/ambiguous/4qrRa/

Ou vous pourriez remove et recréer le this.subView lors du rendu et contourner le problème de cette façon (mais cela pourrait causer d'autres problèmes...).

0 votes

Je ne l'aurais jamais trouvé par moi-même. C'est un peu fou :/. Merci @mu

0 votes

@fguillen : J'ai dû trouver quelque chose de similaire et je pense que cela a nécessité un voyage dans les sources de jQuery et une bonne dose d'expérimentation. Cet effort a gravé de façon permanente dans ma mémoire.

0 votes

Cela m'a conduit à la bonne direction pour résoudre mon problème. Merci @muistooshort

11voto

JMM Points 4032

Il existe une solution plus simple, qui ne fait pas disparaître les inscriptions aux événements : jQuery.detach() .

http://jsfiddle.net/ypG8U/1/

this.subView.render().$el.detach().appendTo( this.$el );   

Cette variante est cependant probablement préférable pour des raisons de performance :

http://jsfiddle.net/ypG8U/2/

this.subView.$el.detach();

this.subView.render().$el.appendTo( this.$el );

// or

this.$el.append( this.subView.render().el );

Il s'agit évidemment d'une simplification qui correspond à l'exemple, où la vue secondaire est le seul contenu du parent. Si c'était vraiment le cas, vous pourriez simplement rendre à nouveau la vue secondaire. S'il y avait d'autres contenus, vous pourriez faire quelque chose comme :

var children = array[];

this.$el.children().detach();

children.push( subView.render().el );

// ...

this.$el.append( children );

ou

_( this.subViews ).each( function ( subView ) {

  subView.$el.detach();

} );

// ...

De plus, dans votre code original, et répété dans la réponse de @mu, un objet DOM est transmis à jQuery.html() mais cette méthode n'est documentée que comme acceptant des chaînes de caractères HTML :

this.$el.html( this.subView.render().el );

Signature documentée pour jQuery.html() :

.html( htmlString )

http://api.jquery.com/html/#html2

1 votes

Très intéressant, une solution plus simple n'est pas le monde que j'utiliserais mais c'est vraiment bon à savoir. Merci

0 votes

Je pense que la question de savoir si c'est "plus simple" du point de vue de la création des classes de vues est discutable. Je pense que c'est probablement plus efficace puisque vous conservez les enregistrements des écouteurs d'événements au lieu de les réappliquer à chaque fois. S'il est possible que vous ayez également des données stockées avec l'élément par l'intermédiaire de jQuery.data() que vous souhaitez préserver lors d'un nouveau rendu, il faut également en tenir compte, car detach() permettra de préserver les données sans code supplémentaire.

0voto

AmpT Points 531

Lorsque vous utilisez $(el).empty() il supprime tous les éléments enfants de l'élément sélectionné ET supprime TOUTES les el événements (et les données) qui sont lié à tout élément (enfant) à l'intérieur de l'élément sélectionné ( el ).

A garder les événements liés à el éléments enfants mais en supprimant les éléments enfants, utilisez :

$(el).children().detach(); au lieu de $(.el).empty();

Cela permettra à votre vue d'être rendue avec succès, les événements étant toujours liés et fonctionnant.

0 votes

Veuillez les consulter pour plus de détails : stackoverflow.com/a/7417078/2728686 y jquerybyexample.net/2012/05/

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X