underscore template backbonejs javascript templates backbone.js

javascript - template - backbonejs github



Backbone.js: repoblar o recrear la vista? (7)

Creo que la mayoría de las personas que comienzan con Backbone crearán la vista como en su código:

var view = new UserDetailView({model:this.model});

Este código crea una vista zombie, porque podríamos crear constantemente una vista nueva sin limpiar la vista existente. Sin embargo, no es conveniente llamar a view.dispose () para todas las vistas de Backbone en su aplicación (especialmente si creamos vistas en for loop)

Creo que el mejor momento para poner el código de limpieza es antes de crear una nueva vista. Mi solución es crear un ayudante para hacer esta limpieza:

window.VM = window.VM || {}; VM.views = VM.views || {}; VM.createView = function(name, callback) { if (typeof VM.views[name] !== ''undefined'') { // Cleanup view // Remove all of the view''s delegated events VM.views[name].undelegateEvents(); // Remove view from the DOM VM.views[name].remove(); // Removes all callbacks on view VM.views[name].off(); if (typeof VM.views[name].close === ''function'') { VM.views[name].close(); } } VM.views[name] = callback(); return VM.views[name]; } VM.reuseView = function(name, callback) { if (typeof VM.views[name] !== ''undefined'') { return VM.views[name]; } VM.views[name] = callback(); return VM.views[name]; }

Usar VM para crear su vista ayudará a limpiar cualquier vista existente sin tener que llamar a view.dispose (). Puede hacer una pequeña modificación a su código de

var view = new UserDetailView({model:this.model});

a

var view = VM.createView("unique_view_name", function() { return new UserDetailView({model:this.model}); });

Por lo tanto, depende de usted si desea reutilizar la vista en lugar de crearla constantemente, siempre que la vista esté limpia, no necesita preocuparse. Simplemente cambie createView para reuseView:

var view = VM.reuseView("unique_view_name", function() { return new UserDetailView({model:this.model}); });

El código detallado y la atribución se publican en https://github.com/thomasdao/Backbone-View-Manager

En mi aplicación web, tengo una lista de usuarios en una tabla a la izquierda y un panel de detalles de usuario a la derecha. Cuando el administrador hace clic en un usuario en la tabla, sus detalles se deben mostrar a la derecha.

Tengo UserListView y UserRowView a la izquierda, y UserDetailView a la derecha. Las cosas funcionan, pero tengo un comportamiento extraño. Si hago clic en algunos usuarios a la izquierda, y luego hago clic en Eliminar en uno de ellos, obtengo cuadros de confirmación de JavaScript sucesivos para todos los usuarios que se han mostrado.

Parece que los enlaces de eventos de todas las vistas visualizadas previamente no se han eliminado, lo que parece ser normal. No debería hacer un nuevo UserDetailView cada vez en UserRowView? ¿Debo mantener una vista y cambiar su modelo de referencia? ¿Debo hacer un seguimiento de la vista actual y eliminarla antes de crear una nueva? Estoy perdido y cualquier idea será bienvenida. Gracias !

Aquí está el código de la vista izquierda (visualización de fila, evento de clic, creación de vista derecha)

window.UserRowView = Backbone.View.extend({ tagName : "tr", events : { "click" : "click", }, render : function() { $(this.el).html(ich.bbViewUserTr(this.model.toJSON())); return this; }, click : function() { var view = new UserDetailView({model:this.model}) view.render() } })

Y el código para la vista correcta (botón eliminar)

window.UserDetailView = Backbone.View.extend({ el : $("#bbBoxUserDetail"), events : { "click .delete" : "deleteUser" }, initialize : function() { this.model.bind(''destroy'', function(){this.el.hide()}, this); }, render : function() { this.el.html(ich.bbViewUserDetail(this.model.toJSON())); this.el.show(); }, deleteUser : function() { if (confirm("Really delete user " + this.model.get("login") + "?")) this.model.destroy(); return false; } })


Esta es una condición común. Si crea una nueva vista cada vez, todas las vistas antiguas seguirán estando vinculadas a todos los eventos. Una cosa que puedes hacer es crear una función en tu vista llamada detatch :

detatch: function() { $(this.el).unbind(); this.model.unbind();

Luego, antes de crear la nueva vista, asegúrese de llamar a detatch en la vista anterior.

Por supuesto, como mencionaste, siempre puedes crear una vista de "detalle" y nunca cambiarla. Puede vincularse al evento "cambiar" en el modelo (desde la vista) para volver a renderizarse usted mismo. Agregue esto a su inicializador:

this.model.bind(''change'', this.render)

Si lo hace, el panel de detalles se volverá a presentar CADA vez que se realice un cambio en el modelo. Puede obtener una granularidad más fina observando una sola propiedad: "change: propName".

Por supuesto, hacer esto requiere un modelo común al que la referencia del objeto hace referencia, así como la vista de lista de nivel superior y la vista de detalles.

¡Espero que esto ayude!


Para arreglar eventos vinculantes varias veces,

$("#my_app_container").unbind() //Instantiate your views here

Usando la línea de arriba antes de instanciar las nuevas Vistas de la ruta, resolvió el problema que tenía con las vistas de zombies.



Siempre destruyo y creo vistas porque a medida que mi aplicación de una sola página se hace cada vez más grande, mantener en memoria las vistas en vivo que no se usan para que pueda volver a usarlas sería difícil de mantener.

Aquí hay una versión simplificada de una técnica que utilizo para limpiar mis Vistas para evitar fugas de memoria.

Primero creo un BaseView del que heredan todas mis vistas. La idea básica es que mi vista guardará una referencia a todos los eventos a los que está suscrito, de modo que cuando sea el momento de deshacerse de la vista, todas esas vinculaciones se desatarán automáticamente. Aquí hay una implementación de ejemplo de mi BaseView:

var BaseView = function (options) { this.bindings = []; Backbone.View.apply(this, [options]); }; _.extend(BaseView.prototype, Backbone.View.prototype, { bindTo: function (model, ev, callback) { model.bind(ev, callback, this); this.bindings.push({ model: model, ev: ev, callback: callback }); }, unbindFromAll: function () { _.each(this.bindings, function (binding) { binding.model.unbind(binding.ev, binding.callback); }); this.bindings = []; }, dispose: function () { this.unbindFromAll(); // Will unbind all events this view has bound to this.unbind(); // This will unbind all listeners to events from // this view. This is probably not necessary // because this view will be garbage collected. this.remove(); // Uses the default Backbone.View.remove() method which // removes this.el from the DOM and removes DOM events. } }); BaseView.extend = Backbone.View.extend;

Cada vez que una Vista necesita vincularse a un evento en un modelo o colección, usaría el método bindTo. Por ejemplo:

var SampleView = BaseView.extend({ initialize: function(){ this.bindTo(this.model, ''change'', this.render); this.bindTo(this.collection, ''reset'', this.doSomething); } });

Cada vez que elimino una vista, simplemente llamo al método de eliminación que limpiará todo automáticamente:

var sampleView = new SampleView({model: some_model, collection: some_collection}); sampleView.dispose();

Compartí esta técnica con la gente que está escribiendo el libro electrónico "Backbone.js on Rails" y creo que esta es la técnica que han adoptado para el libro.

Actualización: 2014-03-24

A partir de Backone 0.9.9, se agregaron listenTo y stopListening a Events utilizando las mismas técnicas bindTo y unbindFromAll que se muestran arriba. Además, View.remove llama a stopListening automáticamente, por lo que vincular y desvincular es tan fácil como esto ahora:

var SampleView = BaseView.extend({ initialize: function(){ this.listenTo(this.model, ''change'', this.render); } }); var sampleView = new SampleView({model: some_model}); sampleView.remove();


Una alternativa es enlazar, en lugar de crear una serie de vistas nuevas y luego desvincular esas vistas. Lo lograrías haciendo algo como:

window.User = Backbone.Model.extend({ }); window.MyViewModel = Backbone.Model.extend({ }); window.myView = Backbone.View.extend({ initialize: function(){ this.model.on(''change'', this.alert, this); }, alert: function(){ alert("changed"); } });

Establecería el modelo de myView en myViewModel, que se establecería en un modelo de usuario. De esta forma, si configura myViewModel para otro usuario (es decir, modificando sus atributos), entonces podría activar una función de representación en la vista con los nuevos atributos.

Un problema es que esto rompe el vínculo con el modelo original. Podría evitar esto utilizando un objeto de colección o configurando el modelo de usuario como un atributo del modelo de vista. Entonces, esto sería accesible en la vista como myview.model.get ("modelo").


Use este método para borrar las vistas secundarias y las vistas actuales de la memoria.

//FIRST EXTEND THE BACKBONE VIEW.... //Extending the backbone view... Backbone.View.prototype.destroy_view = function() { //for doing something before closing..... if (this.beforeClose) { this.beforeClose(); } //For destroying the related child views... if (this.destroyChild) { this.destroyChild(); } this.undelegateEvents(); $(this.el).removeData().unbind(); //Remove view from DOM this.remove(); Backbone.View.prototype.remove.call(this); } //Function for destroying the child views... Backbone.View.prototype.destroyChild = function(){ console.info("Closing the child views..."); //Remember to push the child views of a parent view using this.childViews if(this.childViews){ var len = this.childViews.length; for(var i=0; i<len; i++){ this.childViews[i].destroy_view(); } }//End of if statement } //End of destroyChild function //Now extending the Router .. var Test_Routers = Backbone.Router.extend({ //Always call this function before calling a route call function... closePreviousViews: function() { console.log("Closing the pervious in memory views..."); if (this.currentView) this.currentView.destroy_view(); }, routes:{ "test" : "testRoute" }, testRoute: function(){ //Always call this method before calling the route.. this.closePreviousViews(); ..... } //Now calling the views... $(document).ready(function(e) { var Router = new Test_Routers(); Backbone.history.start({root: "/"}); }); //Now showing how to push child views in parent views and setting of current views... var Test_View = Backbone.View.extend({ initialize:function(){ //Now setting the current view.. Router.currentView = this; //If your views contains child views then first initialize... this.childViews = []; //Now push any child views you create in this parent view. //It will automatically get deleted //this.childViews.push(childView); } });