Ember router: Asynchronous model (promises?)
Asked Answered
H

2

7

[This is about the new 1.0.0-pre.4+ router.]

I want to return from an Ember Route's model method a record that needs asynchronous callbacks to load, for instance because it requires that we load multiple (nested) models. What's the best way to do this?


Here is sample code from a hypothetical blog app that illustrates the problem:

App.Router.map ->
  @resource 'filteredArticles', path: '/:filter'

App.FilteredArticlesRoute = Ember.Route.extend
  model: (params) ->
    blog = App.Blog.find(1) # get the user's Blog singleton
    property = switch params.filter
      when 'published' then 'publishedArticles'
      when 'draft' then 'drafts'
      when 'all' then 'articles'
    # Return the list of articles from the `blog` record.
    # But `blog` hasn't necessarily finished loading :(
    blog.get(property)
Harry answered 11/2, 2013 at 14:42 Comment(6)
As far as I know if you just return blog.get(property) it will return another promise, which will be fetched async.Springclean
I wish it did, but it just returns an empty array that never gets updated -- even when rerunning App.Blog.find(1).get('articles') yields a non-empty array.Harry
That feels more of like a github issue to me :)Springclean
I've had similar issues (but when manipulating content in a controller), and resorted to isLoaded (gist.github.com/domchristie/4774472): a pattern inspired by the Discourse source code. Could you be experiencing this issue?Gombosi
Thanks Dom - the code you linked doesn't solve the problem that the router expects you to return something immediately, but the issue discussion (#1642) looks very interesting.Harry
Jo - yes: I realised it wasn't an ideal piece of code for this question, but thought it may add to the (perhaps more general) issue of how to deal with promises in ember?Gombosi
B
4

I'm in the middle of rewriting Travis CI to the newest ember version and I faced the same problem - we fetch repositories by slug (e.g. emberjs/ember.js), which is not the primary key. My solution involves using Ember.ProxyObject.

When someone enters the path like /emberjs/ember.js, the params will look like:

{ owner: 'emberjs', name: 'ember.js` }

and thus slug will equal emberjs/ember.js.

With such information, I create simple Ember object, which just keeps slug and isLoaded properties:

content = Ember.Object.create slug: slug, isLoaded: false

Then I create a proxy with this object as the content:

proxy = Ember.ObjectProxy.create(content: content)

Now I can load the record from the server using slug and return the proxy as the model. When I get the record from the server, I simply set proxies content to the actual record.

Full solution is here:

deserialize: (params) ->
  slug = "#{params.owner}/#{params.name}"
  content = Ember.Object.create slug: slug, isLoaded: false
  proxy = Ember.ObjectProxy.create(content: content)

  repos = Travis.Repo.bySlug(slug)

  observer = ->
    if repos.get 'isLoaded'
      repos.removeObserver 'isLoaded', observer
      proxy.set 'content', repos.objectAt(0)

  if repos.length
    proxy.set('content', repos[0])
  else
    repos.addObserver 'isLoaded', observer

  proxy

You can also take a look at the rest of the code on github

Barris answered 20/2, 2013 at 14:54 Comment(0)
Q
0

How about you add an observer in the model itself, on the isLoaded status of the model and then call blog.get(property)

blogReady: function() {
  if(this.get('isLoaded') {
     // switch logic
  }
}.observes('isLoaded')
Quadrireme answered 11/2, 2013 at 17:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.