javascript backbone.js firebase backbone.js-collections

javascript - La colección anidada dentro del modelo de la colección firebase no tiene función de agregar



backbone.js backbone.js-collections (1)

En mi aplicación, estoy tratando de usar Firebase para almacenar los datos en tiempo real basados ​​en el backbone framework.

El problema es así:
Tengo un modelo y una colección de subnivel, que son a la vez modelo general y colección.

var Todo = Backbone.Model.extend({ defaults: { title: "New Todo", completed : true } }); var Todocollection = Backbone.Collection.extend({ model: Todo, initialize: function() { console.log("creating a todo collection..."); }, });

Y luego hay un modelo de alto nivel, que contiene la colección de subnivel como un atributo.

var Daymodel = Backbone.Model.extend({ defaults : { day: 1, agenda : new Todocollection() } });

y luego para la colección de nivel superior, haré la colección firebase

var DayCollection = Backbone.Firebase.Collection.extend({ model: Daymodel });

Hasta ahora puedo agregar datos a la colección de nivel superior correctamente, que tiene un atributo de day y un atributo de agenda (que debería ser una TodoCollection ).

El problema es que cuando intento agregar datos a las colecciones de subnivel, no puede funcionar bien.

this.collection.last() .get("agenda") .add({ title: this.input.val(), completed: false });

El código anterior estará dentro de la parte Ver. Y this.collection.last() obtendrá el último modelo. get("agenda") debe ser el objeto de colección.

Pero no puede funcionar. El error muestra que this.collection.last(...).get(...).add no es una función.

Después de la depuración, descubrí que this.collection.last().get("agenda") devuelve un objeto JS general en lugar de un objeto de colección.

Además, depuré eso si uso la colección backbone como la colección externa DayCollection . Todo puede ir bien.

¿Cómo resolver tal problema?


¿Por qué el atributo de colección predeterminado ya no es una colección?

Cuando buscas o creas un nuevo Daymodel que supongo que se ve así:

{ day: 1, agenda : [{ title: "New Todo", completed : false }, { title: "other Todo", completed : false }] }

El atributo de agenda predeterminado que era una Todocollection al principio se reemplaza por una matriz de objetos sin procesar. Backbone no sabe que la agenda es una colección y no la rellena automáticamente.

Esto es lo que hace Backbone con los defaults en la creación del modelo (línea 401) :

var defaults = _.result(this, ''defaults''); attrs = _.defaults(_.extend({}, defaults, attrs), defaults); this.set(attrs, options);

_.extend({}, defaults, attrs) coloca primero los defaults , pero luego los sobrescribe los attrs .

¿Cómo usar una colección dentro de un modelo?

A continuación hay tres soluciones para lograr esto. Utilice solo uno de ellos, o cree el suyo en función de lo siguiente.

La forma más fácil y eficiente es no hacerlo .

Mantenga la Todocollection fuera del modelo Daymodel y solo cree la colección cuando la necesite, como en el hipotético DayView :

var DayView = Backbone.View.extend({ initialize: function() { // create the collection in the view directly this.agenda = new Todocollection(this.model.get(''agenda'')); }, /* ...snip... */ });

Luego, cuando hay cambios que desea que persistan en el modelo, simplemente vuelve a colocar los modelos de colección en el Daymodel :

this.model.set(''agenda'', this.collection.toJSON());

Ponga la colección en una propiedad del modelo.

En lugar de un atributo, puede realizar una función que cree perezosamente la colección y la mantenga dentro del modelo como una propiedad, dejando limpio el hash de los attributes .

var Daymodel = Backbone.Model.extend({ defaults: { day: 1, }, getAgenda: function() { if (!this.agenda) this.agenda = new Todocollection(this.get(''agenda'')); return this.agenda; } });

Luego, el modelo controla la colección y se puede compartir fácilmente con todo lo que ya comparte el modelo, creando solo una colección por instancia.

Al guardar el modelo, aún necesita volver a pasar los modelos sin procesar al hash de attributes .

Una colección dentro de los atributos.

Puede lograr lo que ya está tratando de hacer con pequeños cambios.

  1. Nunca coloque objetos en los defaults

    ... sin usar una función que devuelva un objeto en su lugar.

    var Daymodel = Backbone.Model.extend({ defaults: function() { return { day: 1, agenda: new Todocollection() }; }, });

    De lo contrario, la colección de agenda se compartiría entre todas las instancias de Daymodel ya que la colección se crea solo una vez al crear la clase Daymodel .

    Esto también se aplica a los literales de objetos, matrices, funciones (¿por qué pondrías eso en los defaults todos modos?).

  2. Asegúrese de que siempre sea una colección.

    var Daymodel = Backbone.Model.extend({ defaults: { day: 1, }, initialize: function(attrs, options) { var agenda = this.getAgenda(); if (!(agenda instanceof Todocollection)) { // you probably don''t want a ''change'' event here, so silent it is. return this.set(''agenda'', new Todocollection(agenda), { silent: true }); } }, /** * Parse can overwrite attributes, so you must ensure it''s a collection * here as well. */ parse: function(response) { if (_.has(response, ''agenda'')) { response.agenda = new Todocollection(response.agenda); } return response; }, getAgenda: function() { return this.get(''agenda''); }, setAgenda: function(models, options) { return this.getAgenda().set(models, options); }, });

  3. Asegúrese de que sea serializable.

    var Daymodel = Backbone.Model.extend({ /* ...snip... */ toJSON: function(options) { var attrs = Daymodel.__super__.toJSON.apply(this, arguments), agenda = attrs.agenda; if (agenda) { attrs.agenda = agenda.toJSON(options); } return attrs; }, });

    Esto podría aplicarse fácilmente si coloca la colección en una propiedad modelo como se explicó anteriormente.

  4. Evite anular accidentalmente el atributo de agenda .

    Esto va junto con el punto 2 y ahí es donde se está volviendo difícil, ya que es fácil pasarlo por alto, o alguien más (u otra lib) podría hacerlo más adelante.

    Es posible anular la función save y set para agregar comprobaciones, pero se vuelve demasiado compleja sin mucha ganancia a largo plazo.

¿Cuáles son los contras de la colección en modelos?

Hablé sobre evitarlo completamente dentro de un modelo o crearlo perezosamente. Esto se debe a que puede ser realmente lento si crea una instancia de muchos modelos y más lento si cada modelo se anida varias veces (modelos que tienen una colección de modelos, que tienen otras colecciones de modelos, etc.).

Al crearlo bajo demanda, solo usa los recursos de la máquina cuando lo necesita y solo para lo que se necesita. Cualquier modelo que no esté en la pantalla ahora, por ejemplo, no obtendrá su colección creada.

Soluciones listas para usar

Tal vez sea demasiado trabajo hacer que esto funcione correctamente, por lo que una solución completa podría ayudar y hay un par.