As seems to be the case with most everything I do with ember-data the simple test case is very simple but anything "real" falls apart quickly due to my lack of understanding (and I think documentation). In the simplest case I have no problem creating a new record and saving it to my REST API. I'll use the prevalent fruit basket example and start with just saving a new piece of 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);
Okay, I have no problem here. Conceptually makes sense, the code is clean, and things look peachy! Now suppose that we have a basket
model and that basket
model can hold lots of fruit
. Furthermore, the user can create new fruit
and add it to a new basket
that has not been created yet in one step from the UI.
So here are the new models:
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}) });
Given the above, lets say the user inputs in two new fruits that don't currently exist in the store or in the backend REST API, adds them to his new basket (which also doesn't exist yet) and then hits 'save'.
How do you create this record using Ember Data? I was hoping you could do something like this:
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);
Unfortunately, this doesn't seem to work. When I look at the POST request data in Chrome inspector the fruits
property of basket is always empty. Basically, the request ends up looking this:
{ basket: { fruits: [] } }
Am I using the correct pattern to create the 'basket' record? I'm hoping I don't have to end up saving each piece of fruit first before saving the basket as it seems wasteful to send 3 POST requests when 1 would suffice. Additionally, what happens when the user wants to create 10 pieces of fruit? That would be 11 POST Requests.
Is there a standard practice for accomplishing the above? Essentially, how do you create a new record with a hasMany relationship (basket) where the records in the hasMany relationship are also new (the fruit).
Thank you!
Here are the versions I am using:
------------------------------- Ember : 1.3.2+pre.25108e91 Ember Data : 1.0.0-beta.7+canary.238bb5ce Handlebars : 1.3.0 jQuery : 2.0.3 -------------------------------
Ember has been an enabler of great productivity for many teams for almost a decade and I'm sure it's going to continue to be that. It's changed and improved a lot since its first release and is now in better shape than ever with its Octane edition.
Ember. js is an open source, free JavaScript client-side framework used for developing web applications. It allows building client side JavaScript applications by providing a complete solution which contains data management and an application flow.
It should also be mentioned that Ember is purely a frontend framework. It has a number of ways of interacting with the backend of your choice, but this backend is not in any way handled by Ember itself.
6.3% of javascript developers are currently using Ember. Its popularity has stagnated over the last few years. Ranked 4rd most popular front-end JavaScript framework in State Of JS survey.
I'm just figuring this out myself, but I just did it this way and it works…
For starters, when you this.store.createRecord('basket', {})
it should come with an empty array for its fruits
property. So, do this:
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' }));
Incidentally, the empty fruits
array is actually a DS.PromiseArray
(which may instantly resolve to a DS.ManyArray
?), a more specific subclass of Ember.Array
, so that could make a difference.
However, what I found was that the fruits
property wouldn't get serialized at all--I'm using the JsonApiAdapter though, so that could be the reason. Or it might be that this is the expected behavior when the hasMany
child objects haven't been persisted yet. Anyway, I just did something like this in my controller's actions.save method:
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!'); }); });
Somehow, magically, when all that was done, I ended up with a saved "basket", with a generated id
, and saved "fruits" with generated id
s. And, all the fruits' belongsTo
properties pointed back at the basket, and the basket's hasMany
contained all the fruits…despite the fact that, when I save()
d the basket, the response from my backend returned an empty array for my fruits
property, which I was sure would mess things up but somehow didn't.
Of course, these weren't my actual models, but the parallel was very close. I'm on Ember Data 1.0.0-beta5 and Ember 1.3.1. I'm not using the stock REST adapter, but the JsonApiAdapter is a pretty thin shell over it--so try this approach and see if it works!
I've opened a similar question here that addresses a problem I encountered using the above code to also update existing records (as opposed to creating new ones). That solution is more general and detailed but more complex.
Also, another potential gotcha I ran into is that when (in this example) the basket
is created, the fruits
property (which is a DS.PromiseArray
) may not be fully ready yet. So you may need to do it like this instead:
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', { /* ... */ })); });
In case anyone else is getting to this late, the current documentation points out that you can simply call createRecord()
like this:
post.get('comments').createRecord({})
which works even if post
is an empty object.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With