Backbone.js: cómo desvincularse de los eventos, en la eliminación del modelo
Frecuentes
Visto 12,529 veces
7
en backbone tenemos una aplicación que utiliza un Agregador de eventos, ubicado en el window.App.Events
ahora, en muchas vistas, nos vinculamos a ese agregador, y escribí manualmente una función de destrucción en una vista, que maneja la desvinculación de ese agregador de eventos y luego elimina la vista. (en lugar de eliminar directamente la vista).
ahora, había ciertos modelos en los que también necesitábamos esta funcionalidad, pero no sé cómo abordarla.
ciertos modelos deben vincularse a ciertos eventos, pero tal vez me equivoque, pero si eliminamos un modelo de una colección, permanece en la memoria debido a estos enlaces al agregador de eventos que aún están en su lugar.
no hay realmente una función de eliminación en un modelo, como lo tiene una vista. Entonces, ¿cómo abordaría esto?
EDITAR a petición, algún ejemplo de código.
App = {
Events: _.extend({}, Backbone.Events)
};
var User = Backbone.Model.extend({
initialize: function(){
_.bindAll(this, 'hide');
App.Events.bind('burglar-enters-the-building', this.hide);
},
hide: function(burglarName){
this.set({'isHidden': true});
console.warn("%s is hiding... because %s entered the house", this.get('name'), burglarName);
}
});
var Users = Backbone.Collection.extend({
model: User
});
var House = Backbone.Model.extend({
initialize: function(){
this.set({'inhabitants': new Users()});
},
evacuate: function(){
this.get('inhabitants').reset();
}
});
$(function(){
var myHouse = new House({});
myHouse.get('inhabitants').reset([{id: 1, name: 'John'}, {id: 1, name: 'Jane'}]);
console.log('currently living in the house: ', myHouse.get('inhabitants').toJSON());
App.Events.trigger('burglar-enters-the-building', 'burglar1');
myHouse.evacuate();
console.log('currently living in the house: ', myHouse.get('inhabitants').toJSON());
App.Events.trigger('burglar-enters-the-building', 'burglar2');
});
vea este código en acción en jsFiddle (salida en la consola): http://jsfiddle.net/saelfaer/szvFY/1/
como puede ver, no me conecto a los eventos del modelo, sino a un agregador de eventos. no es necesario desvincular eventos del modelo en sí porque si se elimina, nadie volverá a activar un evento en él. pero el eventAggregator siempre está en su lugar, por la facilidad de pasar eventos a través de toda la aplicación.
el ejemplo de código muestra que incluso cuando se eliminan de la colección, ya no viven en la casa, pero aún ejecutan el comando ocultar cuando un ladrón ingresa a la casa.
3 Respuestas
13
Veo que incluso cuando la dirección del evento vinculante es de esta manera Objeto1 -> escuchar -> Objeto2 hay que quitarlo para Objeto1 perdido cualquier referencia viva.
Y viendo que escuchando a la Modelo remove
evento no es una solución debido a que no se llama en un Collection.reset()
llame entonces tenemos dos soluciones:
1. Sobrescribir la limpieza normal de la colección
As @dira dice aquí puedes sobrescribir Collection._removeReference
para hacer una limpieza más adecuada del método.
No me gustan estas soluciones por dos razones:
- No me gusta sobrescribir un método que tiene que llamar
super
después de. - No me gusta sobrescribir métodos privados.
2. Envolver demasiado su Collection.reset()
llamadas
Que es lo contrario: en vez de sumar funcionalidad más profunda, Añade funcionalidad superior.
Entonces en lugar de llamar Collection.reset()
directamente puede llamar a una implementación que limpiar los modelos antes han sido eliminados silenciosamente:
cleanUp: function( data ){
this.each( function( model ) { model.unlink(); } );
this.reset( data );
}
Una versión clasificadora de su código puede verse así:
AppEvents = {};
_.extend(AppEvents, Backbone.Events)
var User = Backbone.Model.extend({
initialize: function(){
AppEvents.on('my_event', this.listen, this);
},
listen: function(){
console.log("%s still listening...", this.get('name'));
},
unlink: function(){
AppEvents.off( null, null, this );
}
});
var Users = Backbone.Collection.extend({
model: User,
cleanUp: function( data ){
this.each( function( model ) { model.unlink(); } );
this.reset( data );
}
});
// testing
var users = new Users([{name: 'John'}]);
console.log('users.size: ', users.size()); // 1
AppEvents.trigger('my_event'); // John still listening...
users.cleanUp();
console.log('users.size: ', users.size()); // 0
AppEvents.trigger('my_event'); // (nothing)
Consulta el jsFiddle.
Actualización: verificación de que el modelo se eliminó después de eliminar el enlace del evento vinculante
Lo primero que verificamos es que el Objeto1 al escuchar un evento en el Objeto2 crea un enlace en la dirección Objeto2 -> Objeto1:
En la imagen de arriba vemos como el Modelo (@314019) no solo es retenido por el users
colección sino también para la AppEvents
objeto que está observando. parece el vinculación de eventos para la perspectiva de un programador es Objeto que escucha -> a -> Objeto que se escucha pero en realidad es todo lo contrario: Objeto que se escucha -> a -> Objeto que se escucha.
Ahora bien, si usamos el Collection.reset()
para vaciar la Colección vemos como el users
El enlace ha sido eliminado pero el AppEvents
queda el enlace:
La users
el enlace ha desaparecido y también el enlace OurModel.collection
lo que creo que es parte de la Collection._removeReference()
trabajo.
Cuando usamos nuestro Collection.cleanUp()
método el objeto desaparece de la memoria, no puedo hacer que el Chrome.profile
herramienta para decirme explícitamente el objeto @314019 ha sido eliminado pero puedo ver eso ya no está entre los objetos de memoria.
contestado el 23 de mayo de 17 a las 12:05
cuidado estoy usando Backbone v0.9.2
, en tu último jsFiddle había un v5 something
versión de la columna vertebral. - fguillén
Ahora estoy pensando, todavía no estamos seguros de que el Modelo haya sido eliminado de la memoria :).. - fguillén
Acabo de agregar una verificación de que con este enfoque, el modelo se elimina de la memoria. - fguillén
1
Creo que el proceso de referencias limpias es una parte complicada de Backbone
.
Cuando quitas un Model
a partir de una Collection
la Colección se preocupa por unbind
cualquier evento en el Modelo que el Cobro en sí mismo sea vinculante. Verifique este método de colección privado.
Tal vez puedas usar una técnica similar en tu Agregador:
// ... Aggregator code
the_model.on( "remove", this.unlinkModel, this );
// ... more Aggregator code
unlinkModel: function( model ){
model.off( null, null, this );
}
Esto es en el caso de que la dirección de la unión sea Agregador -> Modelo. Si la dirección es la contraria, no creo que tengas que hacer ninguna limpieza después de quitar el modelo.
contestado el 03 de mayo de 12 a las 13:05
no parece que se llame al evento 'eliminar' del modelo en muchas ocasiones, por ejemplo, cuando haces una colección.reset () todos los modelos existentes aquí, no se eliminan... ejemplo: jsfiddle.net/saelfaer/szvFY/4 - Sander
@Sander tienes razón, mi enfoque no funciona en un Collection.reset()
situación. - fguillén
1
En lugar de envolver Collection
's reset
con cleanUp
como sugirió fguillén, prefiero extender Collection
y anulando reset
directamente. La razón es que
cleanUp
surte efecto solo en el código del cliente, pero no en la biblioteca (es decir, Columna vertebral)'s. Por ejemplo, Collection.fetch
puede llamar internamente Collection.reset
. A menos que se modifique el Espina dorsal código fuente, no podemos desvincular modelos de eventos (como en cleanUp
) después de llamar Collection.fetch
.
Básicamente, mi fragmento sugerido es el siguiente:
var MyCollection = Backbone.Collection.extend({
reset: function(models, options) {
this.each(function(model) {
model.unlink(); // same as fguillen's code
});
Backbone.Collection.prototype.reset.apply(this, arguments);
}
});
Más tarde, podemos crear nuevas colecciones basadas en MyCollection
.
Respondido el 07 de Septiembre de 12 a las 11:09
No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas javascript events event-handling backbone.js or haz tu propia pregunta.
Para una mejor comprensión de su pregunta, sería bueno ver algunas líneas de código con ejemplos de cómo y dónde
bind
los eventos del modelo que deseaunbind
cuando se quita el modelo. Ver las direcciones de la vinculación de eventos es muy importante. - fguillen@Sander, el problema se llama Vistas fantasma. Durante
model.destroy()
la vista debería poder vincularse adestroy
evento. Durante el cual los eventos apropiados deben ser delegados y luego la Vista debe ser destruida. - Deeptechtons@Deeptechtons, las vistas fantasma no son un problema, ya que se borraron correctamente, vea mi ejemplo, ni siquiera funciona con vistas en este estado, solo modelos y colecciones, vinculados a un agregador. cuando se eliminan los modelos, todavía existen (vinculados y ejecutándose cuando se llama al evento) - Sander
Veo que la dirección del evento vinculante es Modelo -> Agregador por lo tanto, no hay ninguna referencia de Aggregator a Model, por lo que no tiene nada que limpiar después de eliminar Model. - fguillen
Aparentemente, el modelo aún está vinculado, como puede ver en el ejemplo, aunque las 2 personas se eliminan de la colección de usuarios en la casa, aún ejecutan la función de ocultar cuando se lanza el evento. así que claramente no están eliminados correctamente... y eso es lo que estoy tratando de lograr, desvinculándolos del agregador para que liberen la memoria que usan :) - Sander