47

How can I handle the error

Uncaught Error: No route matched the URL '...'

and show a custom 404 page?


Note: This question was asked before and answered several months ago - but does not work anymore.

Community
  • 1
  • 1
stephanos
  • 3,319
  • 7
  • 33
  • 47

5 Answers5

54
App.Router.map(function() {
  //set up all of your known routes, and then...
  this.route("fourOhFour", { path: "*path"});
});

.. where you have your FourOhFourRoute defined to show the "no route found" message of your choosing. You will be able to access the originally requested path in the fourOhFour route as the path parameter.

EDIT: just for clarity -- this answer came after the others were reported not to work anymore.

EDIT 2: I've updated the answer to reflect Yehuda Katz's comment (if I have it wrong, please LMK).

laurelnaiad
  • 4,558
  • 4
  • 20
  • 18
  • Thank you! When I first tried this, I did indeed miss the colon. This should be documented somewhere! – Pascal Sep 11 '13 at 09:26
  • 9
    The `:` should not be necessary, but a parameter name is (so you can get if from your route if you want). `*path` is what I use. – Yehuda Katz Sep 11 '13 at 14:36
  • 2
    If you are using ember cli your template file should be dasherized (i.e. four-oh-four.hbs) – noctufaber Jun 26 '14 at 21:02
12

Here is an example:

I define the last route in my router using a wildcard route see: http://emberjs.com/guides/routing/defining-your-routes/#toc_wildcard-globbing-routes

I have a /not-found route, see last route defined in my router /*path to catch any text string, see: https://github.com/pixelhandler/blog/blob/master/client/app/router.js#L19

Router.map(function () {
  this.route('about');
  this.resource('posts', function () {
    this.resource('post', { path: ':post_slug' });
  });
  this.resource('admin', function () {
    this.route('create');
    this.route('edit', { path: ':edit_id' });
  });
  this.route('not-found', { path: '/*path' });
});

That route does a redirect to /not-found, see: https://github.com/pixelhandler/blog/blob/master/client/app/routes/not-found.js

import Ember from 'ember';
export default Ember.Route.extend({
  redirect: function () {
    var url = this.router.location.formatURL('/not-found');
    if (window.location.pathname !== url) {
      this.transitionTo('/not-found');
    }
  }
});

Also any route having a hook (e.g. model, beforeModel, afterModel) that results in a rejected promise, can use the error action to transition to the 404.

actions: {
  error: function (error) {
    Ember.Logger.error(error);
    this.transitionTo('/not-found');
  }
}

Which renders a not-found template, see: https://github.com/pixelhandler/blog/blob/master/client/app/templates/not-found.hbs

<h1>404 Not Found</h1>
<p>
  Perhaps you have a link that has changed, see {{#link-to 'posts'}}Archives{{/link-to}}.
</p>

Here is my 404 page: http://pixelhandler.com/not-found

pixelhandler
  • 615
  • 4
  • 11
  • I ended up writing up a post on this subject here: http://pixelhandler.com/posts/how-to-use-404-page-in-your-emberjs-application – pixelhandler Aug 29 '14 at 16:12
  • 2
    Great solution! I found using replaceWith instead of transitionTo was more suitable, otherwise if the user hits the 'Back' button on their browser they will always be redirected back to the /not-found page since they're attempting to go back to the page that was originally not found. – SeanK Dec 29 '14 at 20:21
6

You could try adding a catch-all route at the end of your router:

App.Router.map(function() {
  this.resource('post', ...);
  this.resource('user', ...);
  this.route('catchAll', { path: '/*' });
});

App.CatchAllRoute = ...
James A. Rosen
  • 64,193
  • 61
  • 179
  • 261
  • I actually never though of that - but I would need one "catchAll" for every resource ... works as a workaround for now, but I wonder if there is a better way ... – stephanos Feb 17 '13 at 18:41
1

In Ember 2.x

Inside the App.Router.map function, put code below the the end of the callback function.

this.route('your_handler_route_name', { path: '/*path' });

Now every route does NOT catche by the previous defined routes will be catched by your_handler_route_name route.

XY L
  • 25,431
  • 14
  • 84
  • 143
  • 1
    This is a good answer. But the problem I'm having is that if I have more complex URL (I.E. mysite/good_directory/bad_directory) then I run into difficulties. – Cameron Apr 25 '17 at 00:11
0

Solution 1

To display 404 content:

App.Router.reopen({
        handleURL: function (url) {
            try {
                return this._super(url);
            } catch (error) {
                if (error.message.match(/No route matched the URL/)) {
                    return this._super('/404');
                }
            }
        }
    });

If you want to URL changes to 404 as well:

App.Router.reopen({
        location: locationImplementation,
        handleURL: function (url) {
            try {
                return this._super(url);
            } catch (error) {
                if (error.message.match(/No route matched the URL/)) {
                    this.transitionTo('404');
                    return this._super('/404');
                }
            }
        }
    });

To understand what happened here see line 22636 in ember rc2.

Solution 2

Parse current URL and check if route or resource exist using App.Router.router.recognizer.hasRoute('route.path.goes.here');

Wojciech Bednarski
  • 6,033
  • 9
  • 49
  • 73
  • this looks like a good approach! But the match on the error message seems quite fragile .. – stephanos Apr 07 '13 at 08:59
  • @stephanos it's the best what I found so far. Anyway, in this ember version message wont change, so when upgrade to the rc3 I'll check it. Another option is to override this part of Ember.js - in JS is quite simple, last function wins. – Wojciech Bednarski Apr 07 '13 at 09:28
  • this doesn't seem to work, please provide alternatives, at line: "this._super(url);", there was no exception thrown. perhaps it was delayed by promise pattern. (ember v1.9.1) – Bamboo Feb 04 '15 at 15:11