Resolving multiple sequential ember-data operations

I’m new to both ember-data and promises. How would something like this be done? This is simplified code for an add-to-cart function. I want to get the cart record from the store, add the item to it, save the cart, then return the newly-added item from the cart object all the way back to the calling function.

As it is now, it’s sending back the findRecord promise, and I need it to return the innermost object (under the “// I WANT TO RETURN THIS OBJECT” comment), or present an error if that does not come back.

// calling function
doAdd(item).then(response => {
	successMessage("Successfully added `${response.skuid}`");
}).catch(e => {
	failMessage("Failed to add item `${e.message}`);
});

...

// function with sequential stuff - need to return innermost object
doAdd(item) {

	let self = this;
	
	// get cart record from store
	return this.get('store').findRecord('cart', id).then(cart => {
	
		// add item to items array of cart object
		cart.get('items').addObject(item);
		
		// persist cart back to API
		cart.save().then(newCart => {
		
			// set updated cart object to cart service
			set(this, 'cartObj', newCart);
			
			// send back newly-added item from the cart object
			// I WANT TO RETURN THIS OBJECT TO THE CALLING FUNCTION
			return newCart.get('items').findBy('skuid', item.skuid);
			
		});		
		
	});
}

Hi @midget2000x, you’re actually dealing with some pretty complex ideas here (async data is hard!) but I think you can take advantage of the framework a little more to simplify things.

First, a cart specifically is usually a singleton, and generally “application level” state, so I would recommend maybe making a cart service that:

  • on application init, the service is loaded and the service requests the cart from the store via findRecord or findAll or however your backend handles a cart. This will essentially remove the outermost findRecord and promise in your example above because you’ll always have a reference to the cart model at the application level
  • if you’re using a REST style backend you will probably either want the cart service to poll for cart updates, or just request updates whenever the service is used to do an operation on the cart.

Secondly, I think you may want a return in front of this line:

cart.save().then(newCart => {

e.g.

return cart.save().then(newCart => {

Third, I don’t think you need to do a findBy in your innermost return. I mean you could, but you should just be able to return ‘item’ straight up since you already have a reference to that model (aka ‘item’). So change this:

return newCart.get('items').findBy('skuid', item.skuid);

to:

return item;

Those should actually be equivalent.

A good rule of thumb is that you should try and use the Ember router as much as possible for async data fetching and loading. Obviously there are many examples (like yours) where the use case doesn’t fit and for many of those situations there’s a really great addon called ember-concurrency which can greatly simplify a lot of the async logic necessary to manage async operations.

Thanks @dknutsen, you have been a great help to me. Definitely still in the learning stage! My ember app is kind of a side project I work on when I have time.

First, a cart specifically is usually a singleton, and generally “application level” state, so I would recommend maybe making a cart service that…

Yes, actually I am doing this already (you helped me with it before). The code in my OP is simplified for brevity so doesn’t really mention the architecture.

return cart.save().then(newCart => {

This was the key piece I was missing! The inner return statements get passed up the chain, looks like (or whatever the correct term is)

Third, I don’t think you need to do a findBy in your innermost return

You’re right, my thinking was to make absolutely sure the item was in the cart…get the item from the cart and if it exists, the whole operation is successful. But that’s totally overkill - the item object doesn’t even need to be passed back, the calling function already has it. I really just need to make sure everything resolves, and then show success or fail messages.

there’s a really great addon called ember-concurrency which can greatly simplify a lot of the async logic necessary to manage async operations.

Yes, I’m a little familiar with it. I built an autosuggest with it and it works great. But it’s mostly based on the example code on the e-c site. I’m still wrapping my head around async. Generators are yet to be tackled, but they are on the list!

Ah right I totally remember that now. Glad you got it figured out! I’m only familiar with the basics of EC myself but I know it’s very powerful so I want to play around with it more in the future. Good luck with all your Embering!