|
| 1 | +import Route from '@ember/routing/route'; |
| 2 | +import { inject as service } from '@ember/service'; |
| 3 | + |
| 4 | +/** |
| 5 | + * This is a weird route... but let me explain. |
| 6 | + * |
| 7 | + * This is the default route that gets used if no other matching route is found. |
| 8 | + * |
| 9 | + * This route is *also* used as a generic error page via: |
| 10 | + * |
| 11 | + * ```js |
| 12 | + * this.router.replaceWith('catch-all', { transition, title: 'Something failed' }); |
| 13 | + * ``` |
| 14 | + * |
| 15 | + * Ideally we would use the `error` substate/routes of Ember.js, but those don't |
| 16 | + * update the URL when an error happens. This causes the native back button of the |
| 17 | + * browser to behave in strange way, so we avoid using the broken built-in error |
| 18 | + * routes. |
| 19 | + */ |
| 20 | +export default class CatchAllRoute extends Route { |
| 21 | + @service router; |
| 22 | + |
| 23 | + /** |
| 24 | + * If `transitionTo('catch-all', 'foo')` is used, this hook will not get called. |
| 25 | + * If the second argument is an object, then the second object will be the `model` |
| 26 | + * of this route, and the `serialize()` hook gets called to figure out what the |
| 27 | + * URL of this route should be. The URL is automatically assembled from the passed-in |
| 28 | + * transition object. |
| 29 | + */ |
| 30 | + serialize({ transition }) { |
| 31 | + return { path: this.pathForRouteInfo(transition.to) }; |
| 32 | + } |
| 33 | + |
| 34 | + /** |
| 35 | + * This internal method takes a `RouteInfo` object from Ember.js (e.g. `transition.to`) |
| 36 | + * and returns the corresponding `:path` route parameter for this `catch-all` route. |
| 37 | + * @return {string} |
| 38 | + */ |
| 39 | + pathForRouteInfo(routeInfo) { |
| 40 | + let routeName = routeInfo.name; |
| 41 | + let params = paramsForRouteInfo(routeInfo); |
| 42 | + let queryParams = routeInfo.queryParams; |
| 43 | + return this.router.urlFor(routeName, ...params, { queryParams }).slice(1); |
| 44 | + } |
| 45 | +} |
| 46 | + |
| 47 | +/** |
| 48 | + * Returns all route parameters for the passed-in `RouteInfo` object. |
| 49 | + * |
| 50 | + * These can be used in `router.urlFor(...)` calls. |
| 51 | + */ |
| 52 | +function paramsForRouteInfo(routeInfo) { |
| 53 | + let routeInfos = [...allRouteInfos(routeInfo)].reverse(); |
| 54 | + |
| 55 | + let params = []; |
| 56 | + for (let routeInfo of routeInfos) { |
| 57 | + for (let paramName of routeInfo.paramNames) { |
| 58 | + params.push(routeInfo.params[paramName]); |
| 59 | + } |
| 60 | + } |
| 61 | + return params; |
| 62 | +} |
| 63 | + |
| 64 | +/** |
| 65 | + * Iterates upwards through the `RouteInfo` "family tree" until the top-most |
| 66 | + * `RouteInfo` is reached. |
| 67 | + */ |
| 68 | +function* allRouteInfos(routeInfo) { |
| 69 | + yield routeInfo; |
| 70 | + while ((routeInfo = routeInfo.parent)) { |
| 71 | + yield routeInfo; |
| 72 | + } |
| 73 | +} |
0 commit comments