Override existing catch-all wildcard route


#1

The Ember application I’m working with has a catch-all wildcard route (i.e. *path). I cannot change this; however, I do want to have my own wildcard route. Since the existing route is applied after mine, it overrules my route. Thus, having my own catch-all wildcard route doesn’t work.

How can I register another catch-all wildcard route in this scenario or have my routes behave like one?

I’m able to catch all requests with the following router:

export default function () {
  // Matches single-segment paths.
  this.route('main-route', { path: '/:path' }, function () {
    // Optionally, matches subsequent segments.
    this.route('sub-route', { path: '*wildcard_path' });
  });
}

The problem here is that this directs all multi-segment paths to the route main-route.sub-route. I tried to redirect the route, but I had no success with that. In particular, I need to redirect/transition to main-route in such a way that its beforeModel hook is executed.

The sub-route looks like this:

export default Discourse.Route.extend({
  afterModel(model) {
    this.transitionTo('main-route', model);
  }
});

This transitions to main-route fine.

However, it’s different than for requests targetting the main route directly. Especially, the properties for transition.intent are set differently. There is no transition.intent.url (presumably because I targetted a route name, not a URL) and the URL in the address bar only contains the path segment of the main route without the wildcard_path segment.


I realize that this is a hack/workaround that is unnecessarily complex. If I could remove the existing wildcard route, I wouldn’t need to look for such a solution.

Any pointers that could help me here?


#2

You could use two flat routes:

this.route('main-route', { path: '/:path' });
this.route('main-route-with-more-args', { path: '/:path/*wildcard_path' });

And then reexport the implementation of that route so main-route-with-more-args does the same thing as main-route:

// app/routes/main-route-with-more-args.js
export { default } from './main-route';

// app/templates/main-route-with-more-args.js
export { default } from './main-route'

// app/controllers/main-route-with-more-args.js
export { default } from './main-route';

Alternatively, maybe the nested structure you already have is OK. The beforeModel hook in main-route will fire when you enter either URL. It won’t re-fire if you switch between them, but for that you could use beforeModel in main-route.index and main-route.sub-route.


#3

Ah, thank you. I considered simply duplicating the implementation, but quickly decided against it since I would need to maintain both implementations simultaneously all the time. I did not think about the routes as modules that I could simply re-export.

In my case, the following is sufficient:

// app/routes/main-route-duplicate.js
export { default } from './main-route';

The missing piece now is what you mentioned last: Switching between the paths that are covered by my main route doesn’t seem to trigger hooks I load my content with.

In beforeModel, I fetch more content via AJAX. This doesn’t seem to happen for all transitions. I’ll see whether I can work out how to do this.


#4

When invoking the main route for a multi-segment path and then navigating to a single-segment path, the model hooks are not triggered because main-route is a parent route of main-route.duplicate.

export default function () {
  this.route('main-route', { path: '/:path' }, function () {
    this.route('duplicate', { path: '*wildcard_path' });
  });
}
// app/routes/main-route-duplicate.js
export { default } from './main-route';

Instead, I created two routes on the same level both exporting the implementation of main-route:

export default function () {
  this.route('main-route.single-segment', { path: '/:path' });
  this.route('main-route.multi-segment', { path: '/:path/*wildcard' });
}

So far, this looks very promising.