11

My problem is related with the issues #1183 and #1268 of emberjs.

I have dynamic element at routes. All is ok if I navigate throuht application. The problem is when I reload a page or when a type the url. In that case the app enter in the deserialize function and load and object by their id, but this load is asynchronous.

At issue #1268 lukemelia says "you will need to make the result of your deserialize method implement the promises pattern".

I try it but always loose context. My code is similar to:

page: Ember.Route.extend
route: '/:alias'
deserialize: (router, params) -> page=App.Page.find(params.alias)
$.when( page.get("isLoaded") ).done( () -> console.debug(page.get("alias")) return page)
loading: Em.State.extend

The router goes to loading state but then return with no context data. I think i doing something wrong. Possibly all is wrong.

Can anybody help me? Is there and example?

Thanks!

Resolved:

page: Ember.Route.extend
    route: '/:id'
    deserialize: (router, params) ->
        page=App.Page.find(params.id})
        deferred = $.Deferred()
        page.addObserver("isLoaded", -> deferred.resolve(page))
        return deferred.promise()
    serialize: (router, page) ->
        return {id: page.get("id") }
    connectOutlets: (router, page) ->
        router.get('applicationController').connectOutlet
            context: page
            name: "page"
loading: Em.State.extend
    connectOutlets: (router, context) ->
        router.get('applicationController').connectOutlet(context: context, name: "loading")

While page is loading the active state is loading, when page finish loading, router load page state automatically.

I hope this may help somebody

leroj7
  • 113
  • 1
  • 6

1 Answers1

5

@leroj7 you've figured out a workable solution. Thanks for sharing it. I've created a mixin that I add to models that I need to have this behavior:

Editor.ModelPromise = {
  init: function() {
    this._super();
    this._deferred = $.Deferred();
    this._deferred.promise(this);
    this.one('didLoad', this, '_resolveModelPromise');
    this.one('becameError', this, '_rejectModelPromise');
  },
  _resolveModelPromise: function() {
    this._deferred.resolve(this);
  },
  _rejectModelPromise: function() {
     this._deferred.reject(this);
  }
};

Also available at https://gist.github.com/1b54f0956ba10195a3bc

An approach like this will eventually be baked into ember-data, though it will most likely NOT have a jQuery dependence, since ember-data does not currently depend on jQuery.

Luke Melia
  • 8,389
  • 33
  • 41
  • the observer put on `didLoad` does'nt work, at least on the relationship-improvement branch. I tried it, but it seems that the record is not yet passed in the loaded state, so modifying it will throw an exception like "cannot send event setProperty is state rootState.loading". Did you encounter this ? – sly7_7 Sep 26 '12 at 12:13
  • @sly7_7 I have not seen this. We are using the code I shared in production with the relationship-improvements branch and it is working fine for us. – Luke Melia Sep 27 '12 at 02:19
  • Hum, ok... perhpas I did something wrong... but reading the code https://github.com/emberjs/data/blob/relationship-improvements/packages/ember-data/lib/system/model/states.js#L513 the didLoad is triggered before the model has entered the state loaded, so I the behavior I encounter should happen. This comment https://github.com/emberjs/ember.js/pull/1406#issuecomment-8888168 should be a clue too. – sly7_7 Sep 27 '12 at 07:42
  • @sly7_7 you're correct that in your fiddle, didLoad will have already fired before you start listening for it. That is why the mixin that I shared is a better solution. It will not suffer from that issue. – Luke Melia Sep 27 '12 at 17:03
  • Ok, I can understand, but even by defining the didLoad in the constructor, I reach the same issue: http://jsfiddle.net/Sly7/2VpRh/11/ So perhaps the jQuery.Deferred is doing something special, otherwise, the fact is when didLoad is triggered, the model is not in its loaded state, so modifying it throw this error. Sorry for beeing insistent, but I just want to be sure of what I see. – sly7_7 Sep 27 '12 at 21:53
  • @sly7_7 I'm not sure what the goal of your fiddle is. In order for async routing to work, you need to return a promise from deserialize. In the code I shared, I made the model instance itself that promise. I think you're probably right that didLoad is fired before the model actually leaves the loading state (probably on exit of that state). – Luke Melia Sep 28 '12 at 02:06
  • The goal was to ask if the fact that didLoad is fired before the model leaves the loading could throw this error, including during the routing, especially when the deffered is resolved. – sly7_7 Sep 28 '12 at 06:49
  • @sly7_7 Here's where `didLoad` gets fired: https://github.com/emberjs/data/blob/relationship-improvements/packages/ember-data/lib/system/model/states.js#L517 At that moment, the record is still in the `loading` state, according to my debugger: https://img.skitch.com/20120928-ek79jh57pgx5atdn8thudic57u.jpg If you want to set a property immediately after load, you would probably want to do it inside a `Ember.run.next` – Luke Melia Sep 28 '12 at 14:35
  • Thanks a lot Luke, this should close the discussion. I hope this behavior would be fixed soon. I was often using `Ember.run.next` in my app, I believe it's quite ugly, but for the moment it seems to have no other workaround. Thanks again for taking time to explain all these things :) – sly7_7 Sep 28 '12 at 15:52