web-dev-qa-db-fra.com

Ember.js & Ember Data: comment créer un enregistrement avec une relation hasMany lorsque le parent et l'enfant n'existent pas encore

Comme cela semble être le cas avec presque tout ce que je fais avec les données de braises, le cas de test simple est très simple mais tout ce qui est "réel" s'effondre rapidement en raison de mon manque de compréhension (et je pense que la documentation). Dans le cas le plus simple, je n'ai aucun problème à créer un nouvel enregistrement et à l'enregistrer dans mon REST. Je vais utiliser l'exemple de la corbeille de fruits répandue et commencer par simplement enregistrer un nouveau morceau 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);

D'accord, je n'ai aucun problème ici. Conceptuellement logique, le code est propre et les choses ont l'air peachy! Supposons maintenant que nous ayons un modèle basket et que le modèle basket puisse contenir beaucoup de fruit. De plus, l'utilisateur peut créer un nouveau fruit et l'ajouter à un nouveau basket qui n'a pas encore été créé en une seule étape depuis l'interface utilisateur.

Voici donc les nouveaux modèles:

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})
});

Compte tenu de ce qui précède, disons que l'utilisateur entre dans deux nouveaux fruits qui n'existent pas actuellement dans le magasin ou dans le backend REST, les ajoute à son nouveau panier (qui n'a pas non plus existent encore), puis clique sur "enregistrer".

Comment créez-vous cet enregistrement en utilisant Ember Data? J'espérais que vous pourriez faire quelque chose comme ceci:

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

Malheureusement, cela ne semble pas fonctionner. Lorsque je regarde les données de demande POST dans Chrome inspector, la propriété fruits du panier est toujours vide.) cette:

{
    basket: {
       fruits: []
    }
}

Suis-je en utilisant le modèle correct pour créer l'enregistrement "panier"? J'espère que je n'aurai pas à finir par enregistrer chaque fruit avant de sauvegarder le panier, car il semble inutile d'envoyer 3 POST requêtes lorsque 1 suffirait. De plus, que se passe-t-il lorsque l'utilisateur veut créer 10 fruits? Ce serait 11 POST Requests.

Existe-t-il une pratique standard pour accomplir ce qui précède? Essentiellement, comment créer un nouvel enregistrement avec une relation hasMany (panier) où les enregistrements de la relation hasMany sont également nouveaux (le fruit).

Je vous remercie!

Voici les versions que j'utilise:

------------------------------- 
Ember      : 1.3.2+pre.25108e91 
Ember Data : 1.0.0-beta.7+canary.238bb5ce 
Handlebars : 1.3.0 
jQuery     : 2.0.3 
------------------------------- 
35
Sarus

Je suis en train de comprendre cela moi-même, mais je l'ai fait de cette façon et cela fonctionne…

Pour commencer, lorsque vous this.store.createRecord('basket', {}) il doit être accompagné d'un tableau vide pour sa propriété fruits. Alors, faites ceci:

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'
}));

Par ailleurs, le tableau fruits vide est en fait un DS.PromiseArray (Qui peut instantanément se transformer en un DS.ManyArray?), Une sous-classe plus spécifique de Ember.Array, De sorte que pourrait faire une différence.

Cependant, ce que j'ai trouvé, c'est que la propriété fruits ne serait pas sérialisée du tout - j'utilise cependant JsonApiAdapter, de sorte que puisse être la raison. Ou il se peut que ce soit le comportement attendu lorsque les objets enfants hasMany n'ont pas encore été persistés. Quoi qu'il en soit, je viens de faire quelque chose comme ça dans la méthode actions.save de mon contrôleur:

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!');
    });
});

D'une manière ou d'une autre, comme par magie, quand tout cela a été fait, je me suis retrouvé avec un "panier" enregistré, avec un id généré, et j'ai sauvé des "fruits" avec des ids générés. Et, toutes les propriétés belongsTo des fruits pointaient vers le panier, et le hasMany du panier contenait tous les fruits ... malgré le fait que, lorsque je save() d le panier, la réponse de mon backend a renvoyé un tableau vide pour ma propriété fruits, ce qui, j'en suis sûr, gâcherait les choses, mais en quelque sorte pas.

Bien sûr, ce n'étaient pas mes modèles réels, mais le parallèle était très proche. Je suis sur Ember Data 1.0.0-beta5 et Ember 1.3.1. Je n'utilise pas l'adaptateur REST d'origine, mais le JsonApiAdapter est un shell assez fin par-dessus - alors essayez cette approche et voyez si cela fonctionne!

Éditer

J'ai ouvert un question similaire ici qui résout un problème que j'ai rencontré en utilisant le code ci-dessus pour mettre également à jour les enregistrements existants (au lieu d'en créer de nouveaux). Cette solution est plus générale et détaillée mais plus complexe.

De plus, un autre problème que j'ai rencontré est que lorsque (dans cet exemple) le basket est créé, la propriété fruits (qui est un DS.PromiseArray) Peut ne pas être entièrement prête encore. Vous devrez donc peut-être le faire comme ceci à la place:

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', { /* ... */ }));
});
26
S'pht'Kr

Dans le cas où quelqu'un d'autre arriverait à ce retard, la documentation actuelle souligne que vous pouvez simplement appeler createRecord() comme ceci:

post.get('comments').createRecord({})

qui fonctionne même si post est un objet vide.

17
Dalton A. Mitchell