Linking To Unresolved Subroutes

I have some routes

/teams
/team/:team_id
/team/:team_id/members

Many users only have one team, so they should be able to be linked to their team route. If not, a list of teams is displayed, which link to the /team/:team_id route.

I’m currently handling this with a combination of

  • a Route.redirect hook (if they only have one team, go to the team/:id route) and
  • a list of <LinkTo>s for each team with a dynamic route (either the default destination [/team/:id] or the parameter passed from the redirect param).

I was thinking about adding a redirect query param, but didn’t know if that should be:

  • team.members
  • members
  • /team/:team_id/members
  • .members
  • /members

First, whether to use / (URL convention) or . (Ember convention). Also, whether to include the current path as part of the direct (with a leading dot or slash). Plus, I can see a future need for query params on the team.members route soon (ex /team/:team_id/members?status=inactive).

This seemed to be a perfect fit for RouterService.recognize, but the missing params means I can’t build a full URL to be recognized.

Is there any recommended patterns for deep linking with unresolved params?

Hopefully I’m not misunderstanding your question but let me know if I am

First, whether to use / (URL convention) or . (Ember convention).

Typically when referring to routes you refer to their “name” which is the “.” version e.g. “teams.team.members”. Whenever you use a LinkTo or something like transitionTo you’re using the route name.

Is there any recommended patterns for deep linking with unresolved params?

Not 100% sure what you mean by “unresolved params” as that could mean a couple different things depending on context. If you mean you’re trying to redirect to a child route but you don’t know its dynamic segment (e.g. :id) yet that’s not really something you can do because it changes both the URL that must be visited and the data that must be loaded.

For example if you had a team id and you wanted to redirect to the team members route:

const team = this.store.findRecord('team', params.team_id);
this.transitionTo('teams.team.members', team.id);

but you have to know the team id in order to make that transition.

It’s important to keep in mind that there’s a route name and path are different and how nesting routes affects where your redirects should happen and how.

For example if you have these three URL paths that you want to define for your app:

/teams
/team/:team_id
/team/:team_id/members

one way (not the only way btw) to structure your route map is like:

  this.route('teams', function() {
    // this.route('index'); // implicitly created index route
    this.route('team', { path: '/:id' }, function() {
      // this.route('index'); // implicitly created index route
      this.route('members');
    });
  });

this would actually create the following routes:

name: "teams".              path: "/teams".              dynamic segments: []
name: "teams.index".        path: "/teams".              dynamic segments: []
name: "teams.team"          path: "/teams/:id"           dynamic segments: ["id"]
name: "teams.team.index"    path: "/teams/:id"           dynamic segments: ["id"]
name: "teams.team.members"  path: "/teams/:id/members"   dynamic segments: ["id"]

and it’s important to remember that when using nested routes that will affect how the routes are rendered… meaning teams.index or teams.team cannot be rendered until “teams” is rendered (parents block children from rendering when they are waiting on async things), and child routes will be rendered inside their parents.

of course you could also have a flatter structure:

this.route('teams');
this.route('team', { path: '/teams/:id' });
this.route('members', { path: '/teams/:id/members' });

which results in:

name: "teams".   path: "/teams".              dynamic segments: []
name: "team"     path: "/teams/:id"           dynamic segments: ["id"]
name: "members"  path: "/teams/:id/members"   dynamic segments: ["id"]

How are your routes defined in your router?

1 Like