Backbone.js: cómo desvincularse de los eventos, en la eliminación del modelo

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.

preguntado el 03 de mayo de 12 a las 11:05

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 desea unbind cuando se quita el modelo. Ver las direcciones de la vinculación de eventos es muy importante. -

@Sander, el problema se llama Vistas fantasma. Durante model.destroy() la vista debería poder vincularse a destroy evento. Durante el cual los eventos apropiados deben ser delegados y luego la Vista debe ser destruida. -

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

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. -

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

3 Respuestas

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:

Nuestro objeto se mantiene

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:

Nuestro objeto se conserva 2

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

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

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 or haz tu propia pregunta.