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.
-
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 deDaymodel
ya que la colección se crea solo una vez al crear la claseDaymodel
.Esto también se aplica a los literales de objetos, matrices, funciones (¿por qué pondrías eso en los
defaults
todos modos?). -
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); }, });
-
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.
-
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
yset
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.