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

En mi aplicación, bash usar Firebase para almacenar los datos en tiempo real basados ​​en el marco de la red troncal.

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

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 subniveles como un atributo.

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

y luego para la colección de nivel superior, voy a firebase collection

 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 debe ser una TodoCollection ).

El problema es que cuando bash agregar datos a las colecciones de subniveles, 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 Vista. Y this.collection.last() obtendrá el último modelo. get("agenda") debería 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 encontré que this.collection.last().get("agenda") devuelve un objeto JS general en lugar de un objeto de colección.

Depuré aún más que si uso la colección de la red troncal 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 recuperas o creas un nuevo Daymodel que supongo se ve así:

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

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

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) pone los defaults primero, pero luego, se sobrescriben con los attrs pasados.

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

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

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

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

 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 haya cambios que desee persistir en el modelo, simplemente vuelva 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 crear una función que cree la colección de forma perezosa y la mantenga dentro del modelo como una propiedad, dejando limpios los attributes hash .

 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 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 devuelve 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 cuando se crea la clase Daymodel .

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

  2. Asegúrate 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 agenda .

    Esto va junto con el punto 2 y ahí es donde se está poniendo difícil, ya que es fácil pasar por alto, o alguien más (u otra lib) podría hacer eso en el futuro.

    Es posible anular la función de save y set para agregar cheques, pero se vuelve demasiado complejo sin mucha ganancia en el largo plazo.

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

Hablé de evitarlo dentro de un modelo por completo, o de crearlo perezosamente. Esto se debe a que puede ser realmente lento si crea una gran cantidad de modelos y más lentamente 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 utiliza los recursos de la máquina cuando la necesita y solo para lo que 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 para que esto funcione correctamente, por lo que una solución completa podría ayudar y hay un par.

  • Backbone relacional
  • backbone-nested
  • modelos nesteds de backbone
  • backbone-deep-model
Intereting Posts