Skip to content

Commit 5dc56b8

Browse files
committed
catch-all: Prepare for showing generic errors too
1 parent 4422dda commit 5dc56b8

File tree

4 files changed

+90
-4
lines changed

4 files changed

+90
-4
lines changed

app/controllers/catch-all.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import Controller from '@ember/controller';
2+
import { action } from '@ember/object';
3+
4+
export default class CatchAllController extends Controller {
5+
@action reload(event) {
6+
event.preventDefault();
7+
this.model.transition.retry();
8+
}
9+
}

app/routes/catch-all.js

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
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+
}

app/templates/catch-all.hbs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22
<div local-class="content">
33
{{svg-jar "cuddlyferris" local-class="logo"}}
44

5-
<h1 local-class="title" data-test-title>Page not found</h1>
5+
<h1 local-class="title" data-test-title>{{or @model.title "Page not found"}}</h1>
66

7-
<a href="javascript:history.back()" local-class="link">
8-
Go Back
9-
</a>
7+
{{#if @model.tryAgain}}
8+
<a href="javascript:location.reload()" local-class="link" data-test-try-again {{on "click" this.reload}}>Try Again</a>
9+
{{else}}
10+
<a href="javascript:history.back()" local-class="link" data-test-go-back>Go Back</a>
11+
{{/if}}
1012
</div>
1113
</div>

tests/acceptance/404-test.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ module('Acceptance | 404', function (hooks) {
1313
assert.equal(currentURL(), '/unknown-route');
1414
assert.dom('[data-test-404-page]').exists();
1515
assert.dom('[data-test-title]').hasText('Page not found');
16+
assert.dom('[data-test-go-back]').exists();
17+
assert.dom('[data-test-try-again]').doesNotExist();
1618

1719
await percySnapshot(assert);
1820
});

0 commit comments

Comments
 (0)