Backbone: evento perso nel re-rendering

Ho il super-View che si occupa del rendering delle sotto-viste . Quando eseguo nuovamente il rendering della vista super, tutti gli eventi nelle viste secondarie vengono persi.

Questo è un esempio:

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

Quando clicco nel clic mi div si triggers l’evento. Se eseguo composer.render() nuovo tutto sembra più o meno lo stesso, ma l’ evento click non viene più triggersto.

Controlla il jsFiddle funzionante .

Quando lo fai:

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

Stai dicendo efficacemente questo:

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

e empty uccide gli eventi su tutto ciò che è dentro this.$el :

Per evitare perdite di memoria, jQuery rimuove altri costrutti come dati e gestori di eventi dagli elementi figlio prima di rimuovere gli elementi stessi.

Quindi perdi la chiamata delegate che associa eventi su this.subView e il SubView#render this.subView SubView#render non li riassocura.

È necessario this.subView.delegateEvents() una chiamata this.subView.delegateEvents() in this.$el.html() ma è necessario che avvenga dopo il empty() . Potresti farlo in questo modo:

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

Demo: http://jsfiddle.net/ambiguous/57maA/1/

O in questo modo:

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

Demo: http://jsfiddle.net/ambiguous/4qrRa/

Oppure puoi remove e ricreare this.subView durante il rendering e this.subView il problema in quel modo (ma ciò potrebbe causare altri problemi …).

C’è una soluzione più semplice qui che non jQuery.detach() via le registrazioni degli eventi in primo luogo: jQuery.detach() .

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

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

Questa variazione è probabilmente preferibile per motivi di prestazioni però:

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

 this.subView.$el.detach(); this.subView.render().$el.appendTo( this.$el ); // or this.$el.append( this.subView.render().el ); 

Ovviamente questa è una semplificazione che corrisponde all’esempio, in cui la vista secondaria è l’unico contenuto del genitore. Se fosse davvero così, potresti semplicemente ri-renderizzare la vista secondaria. Se ci fossero altri contenuti potresti fare qualcosa come:

 var children = array[]; this.$el.children().detach(); children.push( subView.render().el ); // ... this.$el.append( children ); 

o

 _( this.subViews ).each( function ( subView ) { subView.$el.detach(); } ); // ... 

Inoltre, nel tuo codice originale, e ripetuto nella risposta di @ mu, un object DOM viene passato a jQuery.html() , ma quel metodo è documentato solo come accettazione di stringhe di HTML:

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

Firma documentata per jQuery.html() :

 .html( htmlString ) 

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

Quando si usa $(el).empty() rimuove tutti gli elementi figli nell’elemento selezionato E rimuove TUTTI gli eventi (e i dati) che sono associati a qualsiasi elemento (figlio) all’interno dell’elemento selezionato ( el ).

Per mantenere gli eventi associati agli elementi secondari , ma rimuovere comunque gli elementi secondari, utilizzare:

$(el).children().detach(); invece di $(.el).empty();

Ciò consentirà la visualizzazione della tua vista correttamente con gli eventi ancora vincolati e funzionanti.