How do I define a default sub-route?

I have a resource, User, within which there are several sections: posts, following, and followers. There is some common logic in the UserRoute, but there isn’t a standalone /users/:id on the site. If you go to /users/:id, I want to redirect (possibly dynamically, based on some information about the user in question) to one of the sub-routes. The only way I’ve figured out how to do this is to have separate resources with a good bit of duplication:

App.Router.map(function() {
  this.resource('users');
  this.route('user-details', { path: "/users/:id/:section" });
});

In UserRoute#setup, I redirect to 'user-details' with the default section.

Of course, I’d prefer to use a nested route:

App.Router.map(function() {
  this.resource('users', function() {
    this.route('details', { path: "/:section" });
  });
});

The problem is that I can’t figure how how to determine – inside UserRoute#setup – whether the route was activated because I went to /users/5633 (in which case I should redirect to /users/5633/posts) or to /users/5633/following (in which case I should just load the user data and let the UserDetailsRoute handle the rendering).

Thinking out loud here but why not make a fake temp User resource under Users and then inside the model hook just ignore the segment id and fire off a transition to else where based on other information you can glean about the user or the parent route. From your description it sounds like you never want to stay on the User route anyway.

This might be abusing the model hook.

You might get tripped up by the promises though.

Or perhaps you could use this dummy UserRoute to always redirect to a hidden intermediate route and then perform some logic in the willTransition action to redirect again once you know what you need to know about the user instance.

It also seems like the intermediateTransitionTo method might hold some utility.

But it feels like perhaps you need to perform a two step transition process and use the final step to ultimately resolve to where you want to be.

These are just guesses reading the API docs. Probably would have to experiment with a jsbin to know for sure if this would work the way I imagine it could.

If I’m understanding you correctly, this is a fairly simple fix. :slight_smile: Every resource has an automatically generated route called index that activates when you go to just the URL of the resource. In your case, if you went to /users/5633, you would be directed to the users.index route. I think the easiest way to redirect the browser would be to override the redirect method of the App.UsersIndexRoute. Then you could make sure you get to the posts, following or followers routes.

in your UserRoute:

afterModel: function(user, transition) {
  if(transition.targetName === this.routeName+'.index') {
    this.transitionTo('whateverNewRouteYouWant');
  }
}

transition.targetName will tell you the last route in the transition. I use this to do exactly what you’re talking about, and it’s worked well so far.

1 Like

It’s definitely not the users.index route I’m looking for – that would be for /users/.

I believe transition.targetName is exactly what I’m looking for. Thanks!

I think default sub-routes should be an optional feature in Ember core eg. in router.js :

Router.map(function() {
  this.route("section", {defaultChild: 'one'}, function() {
    this.route("one");
    this.route("two");
 });
});

Anyway, I made a base class for index routes to redirect to a default sub-route. I’m not sure if it will work with dynamic segments in the original example.

app/utils/index-route-with-default.js :

import Ember from "ember";

var IndexRouteWithDefault = Ember.Route.extend({
  defaultRoute: null,
  beforeModel: function(transition) {
    if (transition.targetName === this.routeName) {
        this.transitionTo(this.routeName.replace(/index$/,this.get('defaultRoute')));
    }
  }
});
export default IndexRouteWithDefault;

usage (app/routes/section/index.js):

import Ember from 'ember';
import IndexRouteWithDefault from 'myapp/utils/index-route-with-default';

export default IndexRouteWithDefault.extend({
  defaultRoute: 'one'
});