Search code examples
javascriptember.jsember-router

Populating content on direct url visit in Ember.js pre-4


Fiddle with the mostly-working code: http://jsfiddle.net/VDA2p/16/

Excerpt of Route believe where the problem lies:

// #/listings/:listing_id/videos
App.VideosRoute = Ember.Route.extend({
    model: function(params) {
        return this.modelFor('listing');
    }
});

If I select a "videos" link, the url is '/listings/1/videos' (for example) and my content loads just fine. If I were to visit this url directly though through the address bar, no videos are retrieved. I've been playing around with calls to serialize() but can't get it working and I'm not sure I'm even on the right path. I've also noticed that once I visit the url directly and get no videos, if I then go directly to the '/listings' url (which loads content just fine) and select any of the "videos" links, the video content is no longer loaded for any of entries. However, if I refresh my browser/app while on the '/listings' url then content exists under the "videos" links.

UPDATE 1:

If I change the original code segment above to instead be

// #/listings/:listing_id/videos
App.VideosRoute = Ember.Route.extend({
    model: function(params) {
    return App.Listings.find(params.listing_id);
    }
});

it works as expected, although I do not understand why.

UPDATE 2:

It did break the "View Video" link however when accessing the '/listings/1/videos' url directly and not via a link. It is now displaying as '/listings/null/videos/1' even though when you click on it the individual video loads just fine. Using the back button takes you back to '/listings/1/videos' where all of the "View Video" links no longer have a 'null' in them.

Attempting to go to a '/listings/1/videos/2' url directly, for example, throws the error of "Error: assertion failed: You used the dynamic segment video_id in your router, but App.Video did not exist and you did not override your state's model hook." which I am now working to hunt down as well. Selecting the "View Video" link though will load the data just fine.


Solution

  • jsFiddle example

    What you want to do is use a nested routing structure. This allows each nested level to do its serializing/deserializing as the route is evaluated.

    For example, if you directly type in the URL /listings/1/videos/2 the App.ListingsRoute's methods are called, then App.ListingRoute's, then App.VideosRoute's, and then finally App.VideosVideoRoute's. In your old router, the flat nature didn't allow for the URL sections /listings or /listings/:listing_id to be properly handled, causing the errors you saw.

    Some of the names of templates and routes are slightly different than the original example due to the added nesting. Check the emberjs routing docs if you want more details on the naming schemes for nested routes.

    The renderTemplate portion of the code tells ember to render the template into the {{outlet}} provided by the application route. The default is to assume there are nested {{outlet}}s in each of the nested routes.

    Router:

    App.Router.map(function() {
        this.resource('listings', function() {
            this.resource('listing', { path: '/:listings_id' }, function(){
                this.resource('videos', function() {
                    this.route('video', { path: '/:videos_id' });
                });
            });
        });
    });
    

    Routes:

    // #/index
    App.IndexRoute = Ember.Route.extend({
        redirect: function(){
                this.transitionTo('listings');
        }
    });
    
    // #/listings
    App.ListingsIndexRoute = Ember.Route.extend({
        model: function() {
            return App.Listings.find();
        },
        renderTemplate: function() {
            this.render({into: 'application'});   
        }
    });
    
    // #/listings/:listing_id
    App.ListingRoute = Ember.Route.extend({});
    
    // #/listings/:listings_id/videos
    App.VideosIndexRoute = Ember.Route.extend({
        model: function(params) {
            return this.modelFor('listing').get("videos");
        },
        renderTemplate: function() {
            this.render({into: 'application'});   
        }
    });
    
    // #/listings/:listings_id/videos/:videos_id
    App.VideosVideoRoute = Ember.Route.extend({
        renderTemplate: function() {
            this.render({into: 'application'});   
        }
    })