createrecord - Ember.js & Ember Data: Cómo crear un registro con una relación hasMany cuando el padre y el hijo aún no existen
ember serializer (2)
En caso de que alguien más llegue tarde, la documentación actual indica que simplemente puede llamar a createRecord()
esta manera:
post.get(''comments'').createRecord({})
que funciona incluso si la post
es un objeto vacío.
Como parece ser el caso con la mayoría de todo lo que hago con ember-data, el caso de prueba simple es muy simple, pero cualquier cosa "real" se desmorona rápidamente debido a mi falta de comprensión (y creo que la documentación). En el caso más simple, no tengo problemas para crear un nuevo registro y guardarlo en mi API REST. Usaré el ejemplo prevalente de la cesta de frutas y comenzaré con solo guardar una nueva pieza de fruit
.
// Here is our fruit model
module.exports = App.Fruit= DS.Model.extend({
name: attr(''string''),
color: attr(''string'')
});
// This is easy to create and save
var fruit = this.store.createRecord(''fruit'', {
name: ''apple'',
color: ''red''
});
fruit.save().then(successCallback, errorCallback);
De acuerdo, no tengo ningún problema aquí. ¡Conceptualmente tiene sentido, el código está limpio y las cosas se ven perfectas! Ahora supongamos que tenemos un modelo de basket
y que el modelo de basket
puede contener mucha fruit
. Además, el usuario puede crear nuevas fruit
y agregarlas a una nueva basket
que aún no se ha creado en un solo paso desde la interfaz de usuario.
Así que aquí están los nuevos modelos:
module.exports = App.Fruit= DS.Model.extend({
name: attr(''string''),
color: attr(''string'')
basket: DS.belongsTo(''basket'')
});
module.exports = App.Basket = DS.Model.extend({
fruits: DS.hasMany(''fruit'', {async:true})
});
Teniendo en cuenta lo anterior, supongamos que el usuario introduce dos nuevas frutas que no existen actualmente en la tienda o en la API REST de fondo, las agrega a su nueva cesta (que tampoco existe todavía) y luego pulsa ''guardar'' .
¿Cómo se crea este registro usando Ember Data? Esperaba que pudieras hacer algo como esto:
var newFruits = Ember.A();
newFruits.pushObject(this.store.createRecord(''fruit'', {
name: ''orange'',
color: ''orange''
}));
newFruits.pushObject(this.store.createRecord(''fruit'', {
name: ''banana'',
color: ''yellow''
}));
var basket = this.store.createRecord(''basket'', {
fruits: newFruits
});
basket.save().then(successCallback, errorCallback);
Desafortunadamente, esto no parece funcionar. Cuando miro los datos de solicitud de POST en el inspector de Chrome, la propiedad de fruits
de la cesta siempre está vacía. Básicamente, la solicitud termina mirando esto:
{
basket: {
fruits: []
}
}
¿Estoy usando el patrón correcto para crear el registro ''cesta''? Espero no tener que terminar guardando cada pieza de fruta antes de guardar la cesta, ya que parece un desperdicio enviar 3 solicitudes POST cuando 1 sea suficiente. Además, ¿qué sucede cuando el usuario desea crear 10 piezas de fruta? Eso sería 11 Solicitudes POST.
¿Existe una práctica estándar para lograr lo anterior? Esencialmente, ¿cómo se crea un nuevo registro con una relación hasMany (cesta) donde los registros en la relación hasMany también son nuevos (la fruta).
¡Gracias!
Estas son las versiones que estoy usando:
-------------------------------
Ember : 1.3.2+pre.25108e91
Ember Data : 1.0.0-beta.7+canary.238bb5ce
Handlebars : 1.3.0
jQuery : 2.0.3
-------------------------------
Solo estoy averiguando esto, pero lo hice de esta manera y funciona ...
Para empezar, cuando this.store.createRecord(''basket'', {})
debería venir con una matriz vacía para su propiedad de fruits
. Entonces, haz esto:
var basket = this.store.createRecord(''basket'', {});
basket.get(''fruits'').addObject(this.store.createRecord(''fruit'', {
name: ''orange'',
color: ''orange''
}));
basket.get(''fruits'').addObject(this.store.createRecord(''fruit'', {
name: ''banana'',
color: ''yellow''
}));
Por cierto, el conjunto de fruits
vacíos es en realidad un DS.PromiseArray
(que puede resolverse instantáneamente en un DS.ManyArray
?), Una subclase más específica de Ember.Array
, por lo que podría marcar la diferencia.
Sin embargo, lo que encontré fue que la propiedad de fruits
no se serializaría en absoluto; sin embargo, estoy usando JsonApiAdapter, por lo que esa podría ser la razón. O podría ser que este es el comportamiento esperado cuando todavía no se han hasMany
los objetos hijo hasMany
. De todos modos, acabo de hacer algo como esto en el método actions.save de mi controlador:
basket.save().then(function(){
var promises = Ember.A();
basket.get(''fruits'').forEach(function(item){
promises.push(item.save());
});
Ember.RSVP.Promise.all(promises).then(function(resolvedPromises){
alert(''All saved!'');
});
});
De alguna manera, mágicamente, cuando todo estaba hecho, terminé con una "canasta" guardada, con una id
generada, y "frutas" guardadas con id
generadas. Y, todas las frutas belongsTo
propiedades apuntadas hacia la canasta, y la canasta contiene hasMany
frutas ... a pesar de que, cuando hasMany
la canasta, la respuesta de mi backend devolvió una matriz vacía para mi propiedad de fruits
, lo cual estaba seguro que ensuciaría las cosas, pero de alguna manera no fue así.
Por supuesto, estos no eran mis modelos reales, pero el paralelo era muy cercano. Estoy en Ember Data 1.0.0-beta5 y Ember 1.3.1. No estoy usando el adaptador REST estándar, pero el adaptador JsonApiAdapter es bastante delgado, así que pruebe este enfoque y vea si funciona.
Editar
He abierto una pregunta similar aquí que resuelve un problema que encontré utilizando el código anterior para actualizar también registros existentes (en lugar de crear nuevos). Esa solución es más general y detallada, pero más compleja.
Además, otro error potencial que encontré es que cuando (en este ejemplo) se crea la basket
, la propiedad de fruits
(que es una DS.PromiseArray
) puede no estar completamente lista aún. Por lo tanto, es posible que deba hacerlo así en su lugar:
var basket = this.store.createRecord(''basket'', {});
basket.get(''fruits'').then(function(){
// Now the fruits array is available
basket.get(''fruits'').addObject(this.store.createRecord(''fruit'', { /* ... */ }));
});