Problem with asyn routing #1268

Closed
cristobal151 opened this Issue Aug 13, 2012 · 14 comments

Comments

Projects
None yet
10 participants
@cristobal151

Hi,
I have this code (http://jsfiddle.net/cristobal151/GCSdd/1/):

(function() {
    /**
     * App
     */
    App = Ember.Application.create({});
    App.ApplicationController = Ember.Controller.extend({
        updateTitle: function updateTitle(title) {
            document.title = title;
        }
    });
    App.ApplicationView = Ember.View.extend({
        templateName: 'layout'
    });

    /**
     * Menu
     */
    App.MenuController = Ember.ArrayController.extend({});
    App.MenuView = Ember.View.extend({
        templateName: 'menu'
    });
    App.MenuItem = Ember.Object.extend({});

    /**
     * Posts list
     */
    App.PostsController = Ember.ArrayController.extend({
        content     : [],

        init: function() {
            var _self = this;

            _self._super();
            _self.updatePosts();
        },
        updatePosts: function() {
            var _self    = this;

            var url     = 'url';
            $.getJSON(url + '&callback=?', function(result) {
                var posts = [];
                $.each(result.items, function(i, post) {
                    posts.pushObject(App.PostController.create(post));
                });
                _self.set('content', posts);
            });
        }
    });
    App.PostsView = Ember.View.extend({
        templateName: 'posts'
    });

    /**
     * Post
     */
    App.PostController = Ember.ObjectController.extend({
        year: function() {
            var updated_at = this.get('updated_at');
            return moment(updated_at).format('YYYY');
        }.property('updated_at'),
        month: function() {
            var updated_at = this.get('updated_at');
            return moment(updated_at).format('MM');
        }.property('updated_at'),
        find: function(slug) {
            var _self = this;
            var url = 'url';

            $.getJSON(url + '?callback=?', function(post) {
                _self.set('content', post);
            });
        }
    });
    App.PostView = Ember.View.extend({
        templateName: 'post',
        didInsertElement: function() {
            $(window).scrollTop(0);
        }
    });

    /**
     * Router
     */
    App.Router = Ember.Router.extend({
        location: 'history',
        enableLogging: true,
        root: Ember.Route.extend({
            showHome: Ember.State.transitionTo('index.home'),
            showPostsInDimension: Ember.State.transitionTo('index.dimension'),

            index: Ember.Route.extend({
                route: '/',                
                showPost: Ember.State.transitionTo('post'),                
                connectOutlets: function(router) {
                    var applicationController = router.get('applicationController');

                    applicationController.connectOutlet('menu', 'menu');
                },
                home: Ember.Route.extend({
                    route: '/',
                    connectOutlets: function(router) {
                        var applicationController = router.get('applicationController');
                        var homeController = router.get('homeController');

                        homeController.connectOutlet('posts', 'posts');
                        applicationController.connectOutlet('home');
                    }
                }),
                post: Ember.Route.extend({
                    route: '/:year/:month/:slug',

                    connectOutlets: function(router, post) {
                        router.get('applicationController').connectOutlet('post', post);
                    },
                    serialize: function (router, post) {
                        return {
                            "year": post.get('year'),
                            "month": post.get('month'),
                            "slug": post.get('slug')
                        }
                    },
                    deserialize: function (router, params) {
                        router.get('postController').find(params['slug']);
                    }
                })
            })
        })
    });

    $(function() {
        App.initialize();
    });
})();

It work when i load the list of post and then go to one post, but when a want to enter with the url of a post, the serialize and derialize dont wait to load the post from an api y send me an error.

How can i asyn a rout when enter for the first time in the url??

Thanks

@lukemelia

This comment has been minimized.

Show comment
Hide comment
@lukemelia

lukemelia Aug 13, 2012

Member

PR #1183, which was merged a few weeks ago, offers a solution to your problem. To take advantage of it, you will need to make the result of your deserialize method implement the promises pattern.

Member

lukemelia commented Aug 13, 2012

PR #1183, which was merged a few weeks ago, offers a solution to your problem. To take advantage of it, you will need to make the result of your deserialize method implement the promises pattern.

@wagenet wagenet closed this Aug 14, 2012

@cristobal151

This comment has been minimized.

Show comment
Hide comment
@cristobal151

cristobal151 Aug 14, 2012

Sorry for that issue, i look, but don't fine any things.
Can you make and example of this??, i can't fine how to do it.

Thanks

Sorry for that issue, i look, but don't fine any things.
Can you make and example of this??, i can't fine how to do it.

Thanks

@leroj7

This comment has been minimized.

Show comment
Hide comment
@leroj7

leroj7 Aug 17, 2012

I need help too, please. May be you create a fiddle?
Thanks

leroj7 commented Aug 17, 2012

I need help too, please. May be you create a fiddle?
Thanks

@leroj7

This comment has been minimized.

Show comment
Hide comment
@leroj7

leroj7 Aug 21, 2012

I finally get it:

deserialize: (router, params) ->
    page=App.Page.find(params.id})
    deferred = $.Deferred()
    page.addObserver("isLoaded", -> deferred.resolve(page))
    return deferred.promise()

While 'page' is loading the router goes to state 'loading'. I defined that state in the router:

loading: Em.State.extend
    connectOutlets: (router, context) ->
        router.get('applicationController').connectOutlet(context: context, name: "loading")

I created a view for loading whith an ajax loader gif. All work ok.

Good Job Wagenet.

leroj7 commented Aug 21, 2012

I finally get it:

deserialize: (router, params) ->
    page=App.Page.find(params.id})
    deferred = $.Deferred()
    page.addObserver("isLoaded", -> deferred.resolve(page))
    return deferred.promise()

While 'page' is loading the router goes to state 'loading'. I defined that state in the router:

loading: Em.State.extend
    connectOutlets: (router, context) ->
        router.get('applicationController').connectOutlet(context: context, name: "loading")

I created a view for loading whith an ajax loader gif. All work ok.

Good Job Wagenet.

@cristobal151

This comment has been minimized.

Show comment
Hide comment
@cristobal151

cristobal151 Aug 21, 2012

can you post a fiddle example??, i can't make works.

Thanks

can you post a fiddle example??, i can't make works.

Thanks

@mutewinter

This comment has been minimized.

Show comment
Hide comment
@mutewinter

mutewinter Aug 22, 2012

Contributor

@leroj7 that worked perfectly for me, thanks for sharing that code

Contributor

mutewinter commented Aug 22, 2012

@leroj7 that worked perfectly for me, thanks for sharing that code

@trek

This comment has been minimized.

Show comment
Hide comment
@trek

trek Aug 22, 2012

Member

@wagenet can you say whether @leroj7's solution is the intende usage? I feel like any object that implements then could be the return object and not just jQuery.Deferred objects.

Member

trek commented Aug 22, 2012

@wagenet can you say whether @leroj7's solution is the intende usage? I feel like any object that implements then could be the return object and not just jQuery.Deferred objects.

@lukemelia

This comment has been minimized.

Show comment
Hide comment
@lukemelia

lukemelia Sep 10, 2012

Member

@trek: I would say that @leroj7's solution is one possible approach. The requirement is the deserialize return something that as a then method which conforms to the Promises/A pattern and calls the success handler with the params that should be passed to connect outlet.

Member

lukemelia commented Sep 10, 2012

@trek: I would say that @leroj7's solution is one possible approach. The requirement is the deserialize return something that as a then method which conforms to the Promises/A pattern and calls the success handler with the params that should be passed to connect outlet.

@krisselden

This comment has been minimized.

Show comment
Hide comment
@krisselden

krisselden Sep 10, 2012

Member

Here is a fiddle http://jsfiddle.net/krisselden/uErrd/ if you want to see the urls you can go directly to http://jsfiddle.net/krisselden/uErrd/show/

When routes are nested, you need a loading state at each level.

Member

krisselden commented Sep 10, 2012

Here is a fiddle http://jsfiddle.net/krisselden/uErrd/ if you want to see the urls you can go directly to http://jsfiddle.net/krisselden/uErrd/show/

When routes are nested, you need a loading state at each level.

@shwoodard

This comment has been minimized.

Show comment
Hide comment
@shwoodard

shwoodard Sep 19, 2012

Contributor

I have a loading state at each level. Sometimes, the router makes to the later states; sometimes, it hangs at root.loading--never makes it to nested routes. Any ideas?

Contributor

shwoodard commented Sep 19, 2012

I have a loading state at each level. Sometimes, the router makes to the later states; sometimes, it hangs at root.loading--never makes it to nested routes. Any ideas?

@shwoodard

This comment has been minimized.

Show comment
Hide comment
@shwoodard

shwoodard Sep 19, 2012

Contributor

UPDATE

The loading object may already be loaded when serialize is called so do,

deserialize: (router, params) ->
  report = @_super(router, params)
  deferred = $.Deferred()
  if report.get('isLoaded')
    deferred.resolve(report)
  else
    report.one 'didLoad', ->
      deferred.resolve(report)
  deferred.promise()
Contributor

shwoodard commented Sep 19, 2012

UPDATE

The loading object may already be loaded when serialize is called so do,

deserialize: (router, params) ->
  report = @_super(router, params)
  deferred = $.Deferred()
  if report.get('isLoaded')
    deferred.resolve(report)
  else
    report.one 'didLoad', ->
      deferred.resolve(report)
  deferred.promise()
@ferdy-lw

This comment has been minimized.

Show comment
Hide comment
@ferdy-lw

ferdy-lw Sep 20, 2012

Does not adding an observer cause a leak? Will the observer be automatically torn down?

page.addObserver("isLoaded", -> deferred.resolve(page))

Does not adding an observer cause a leak? Will the observer be automatically torn down?

page.addObserver("isLoaded", -> deferred.resolve(page))
@heartsentwined

This comment has been minimized.

Show comment
Hide comment
@heartsentwined

heartsentwined Mar 31, 2013

Contributor

Is there a solution for this with the new router?

I have read #1378, #1183, ghempton's gist, lukemelia's gist, and this SO question, but they all refer to the old router.

Contributor

heartsentwined commented Mar 31, 2013

Is there a solution for this with the new router?

I have read #1378, #1183, ghempton's gist, lukemelia's gist, and this SO question, but they all refer to the old router.

@heartsentwined

This comment has been minimized.

Show comment
Hide comment
@heartsentwined

heartsentwined Apr 10, 2013

Contributor

Solved. Posting solution here just in case anybody comes across this:

App.FoosIndexController = Em.ArrayController.extend
  someAction: ->
    foo = App.Foo.find(1) # id might as well come from somewhere
    if foo.isLoaded
      @transitionToRoute 'foos.show', foo
    else
      foo.one 'didLoad', => @transitionToRoute 'foos.show', foo
Contributor

heartsentwined commented Apr 10, 2013

Solved. Posting solution here just in case anybody comes across this:

App.FoosIndexController = Em.ArrayController.extend
  someAction: ->
    foo = App.Foo.find(1) # id might as well come from somewhere
    if foo.isLoaded
      @transitionToRoute 'foos.show', foo
    else
      foo.one 'didLoad', => @transitionToRoute 'foos.show', foo
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment