psr7-demo
psr7-demo copied to clipboard
Add async control to getters
Here is the basic problem I'm having with hasMany relationships. I have an action on a route that needs to set not only direct properties but properties on certain the the hasMany children. With emberFire, despite the many annoyances coming from Ember-Data, this trivial because there are Promises everywhere. So I can do this:
AccountRoute = Ember.Route.extend
model: (params) -> @store.find "account", params.account_id
actions:
invest: (account) ->
account.set "status", "Invested"
account.get("tickets").then((tickets) ->
newTickets = tickets.filterBy "type", "New Account"
promises = newTickets.map (ticket) ->
ticket.set "status", "Processed"
ticket.save()
Ember.RSVP.allSettled(promises).then ->
account.save().then ->
toastr.success "Account Invested"
But, currently, with fireplace, I get stuck:
AccountRoute = Ember.Route.extend
model: (params) -> @store.fetch "account", params.account_id
actions:
invest: (account) ->
account.set "status", "Invested"
tickets = account.get("tickets")
# at this point, the ticket models haven't completely resolved
# attempts to filter, map etc. fail
# and without a Promise interface, I'm dead in the water
account.save().then ->
toastr.success "Account Invested"
What I'm gathering is that fireplace is currently set up well to work when you're dealing with properties in templates that can update on their own time, and can deal with being unresolved for some short period (though even that leads to some "flickering" when data pours in after a route has rendered that you don't get if you can use promises to pause the transition). However, in cases like this, where you need to act on the relationships outside of templates, the lack of async handling is a real obstacle.
Definitely agree, this shouldn't be too tricky to resolve, I just need to give it a little more thought as to how to do it cleanly.
I ran into this myself yesterday where I didn't want a page to finish loading until all the associated items had loaded.
There's a hacky workaround which I used then which relies on the fact that collection members are promise models, so you can do wait for all those to load but completely agree this should be nicer.
Also, if you use a promise when loading the collection then it should return the actual items instead of them being wrapped in promise models.
Here's the hacky workaround I'm using in my app to ensure the items are all loaded when the page loads, something similar should work for your usecase.
AccountRoute = Ember.Route.extend
model: (params) ->
@store.fetch("account", params.account_id)
afterModel: (account) ->
Ember.RSVP.all(account.get("tickets").mapBy("content"))
Thank you for the quick response. The good news is that I wasn't just missing something obvious about the API. That's a clever hack; I'll give it a try. Also, if there's any way I can help contribute to a solution, let me know.
I tried the hack, but unfortunately, it didn't work. What I figured out is that I think you meant to say mapBy("promise") instead of mapBy("content"). In any case, when I did that, it worked, which makes sense.
I've made some progress on this (sorry for the hiatus, busy year!).
The promise returned from store.fetch("foo") now is only resolved when its items are loaded, especially important for indexed collections. That covers one category of issues.
Collections now implement a fetch method which returns a promise (the same as when calling fetch from the store) which means you can now do:
account.get("tickets").fetch().then(function() {
// tickets are loaded
});
The final step is for the collection to implement then or be a promise proxy so that you can just do:
account.get("tickets").then(function() {
// tickets are loaded
});
This will still work lazily in templates with {{#each ticket in account.tickets}} etc...
That bit should be completed in the next few days and I'll release in ~0.2.1
:+1: