diff --git a/public/docs/_examples/router/e2e-spec.ts b/public/docs/_examples/router/e2e-spec.ts index cb5d05a5a7..f0c79e2dfc 100644 --- a/public/docs/_examples/router/e2e-spec.ts +++ b/public/docs/_examples/router/e2e-spec.ts @@ -31,6 +31,8 @@ describe('Router', function () { loginHref: hrefEles.get(3), loginButton: element.all(by.css('my-app > ng-component > p > button')), + sidekicksButton: element.all(by.css('my-app > ng-component > button')), + }; } @@ -118,6 +120,15 @@ describe('Router', function () { }); }); + it('should be able to handle 404 pages', function () { + let page = getPageStruct(); + page.heroesHref.click().then(function() { + return page.sidekicksButton.click(); + }).then(function() { + expect(page.routerTitle.getText()).toContain('Page Not Found'); + }); + }); + function crisisCenterEdit(index: number, shouldSave: boolean) { let page = getPageStruct(); let crisisEle: ElementFinder; diff --git a/public/docs/_examples/router/ts/app/app-routing.module.1.ts b/public/docs/_examples/router/ts/app/app-routing.module.1.ts index 8b5f6983f5..b8fb83f0eb 100644 --- a/public/docs/_examples/router/ts/app/app-routing.module.1.ts +++ b/public/docs/_examples/router/ts/app/app-routing.module.1.ts @@ -5,10 +5,12 @@ import { RouterModule, Routes } from '@angular/router'; import { CrisisListComponent } from './crisis-list.component'; import { HeroListComponent } from './hero-list.component'; +import { PageNotFoundComponent }from './not-found.component'; const appRoutes: Routes = [ { path: 'crisis-center', component: CrisisListComponent }, - { path: 'heroes', component: HeroListComponent } + { path: 'heroes', component: HeroListComponent }, + { path: '**', component: PageNotFoundComponent } ]; @NgModule({ diff --git a/public/docs/_examples/router/ts/app/app-routing.module.2.ts b/public/docs/_examples/router/ts/app/app-routing.module.2.ts index 53f0f24056..d2e6fd8943 100644 --- a/public/docs/_examples/router/ts/app/app-routing.module.2.ts +++ b/public/docs/_examples/router/ts/app/app-routing.module.2.ts @@ -4,9 +4,11 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { CrisisListComponent } from './crisis-list.component'; +import { PageNotFoundComponent }from './not-found.component'; const appRoutes: Routes = [ - { path: 'crisis-center', component: CrisisListComponent } + { path: 'crisis-center', component: CrisisListComponent }, + { path: '**', component: PageNotFoundComponent } ]; @NgModule({ diff --git a/public/docs/_examples/router/ts/app/app-routing.module.3.ts b/public/docs/_examples/router/ts/app/app-routing.module.3.ts index 5e8a08e40a..16d9d4ae59 100644 --- a/public/docs/_examples/router/ts/app/app-routing.module.3.ts +++ b/public/docs/_examples/router/ts/app/app-routing.module.3.ts @@ -1,10 +1,11 @@ // #docplaster // #docregion -import { NgModule } from '@angular/core'; +import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; +import { PageNotFoundComponent }from './not-found.component'; const appRoutes: Routes = [ - + { path: '**', component: PageNotFoundComponent } ]; @NgModule({ diff --git a/public/docs/_examples/router/ts/app/app-routing.module.4.ts b/public/docs/_examples/router/ts/app/app-routing.module.4.ts index 7ea7cb11ed..e5da4acdd1 100644 --- a/public/docs/_examples/router/ts/app/app-routing.module.4.ts +++ b/public/docs/_examples/router/ts/app/app-routing.module.4.ts @@ -2,11 +2,12 @@ // #docregion import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; +import { PageNotFoundComponent }from './not-found.component'; import { CanDeactivateGuard } from './can-deactivate-guard.service'; const appRoutes: Routes = [ - + { path: '**', component: PageNotFoundComponent } ]; @NgModule({ diff --git a/public/docs/_examples/router/ts/app/app-routing.module.5.ts b/public/docs/_examples/router/ts/app/app-routing.module.5.ts index 33505b532b..5791c4c4e0 100644 --- a/public/docs/_examples/router/ts/app/app-routing.module.5.ts +++ b/public/docs/_examples/router/ts/app/app-routing.module.5.ts @@ -4,8 +4,8 @@ import { NgModule } from '@angular/core'; // #docregion import-router import { RouterModule, Routes } from '@angular/router'; // #enddocregion import-router - -import { CanDeactivateGuard } from './can-deactivate-guard.service'; +import { PageNotFoundComponent } from './not-found.component'; +import { CanDeactivateGuard } from './can-deactivate-guard.service'; // #docregion can-load-guard import { AuthGuard } from './auth-guard.service'; // #enddocregion can-load-guard @@ -19,7 +19,8 @@ const appRoutes: Routes = [ // #enddocregion lazy-load-admin canLoad: [AuthGuard] // #docregion lazy-load-admin - } + }, + { path: '**', component: PageNotFoundComponent } ]; @NgModule({ diff --git a/public/docs/_examples/router/ts/app/app-routing.module.6.ts b/public/docs/_examples/router/ts/app/app-routing.module.6.ts index c9f1f2916c..e44382979a 100644 --- a/public/docs/_examples/router/ts/app/app-routing.module.6.ts +++ b/public/docs/_examples/router/ts/app/app-routing.module.6.ts @@ -8,8 +8,9 @@ import { // #docregion preload-v1 } from '@angular/router'; -import { CanDeactivateGuard } from './can-deactivate-guard.service'; -import { AuthGuard } from './auth-guard.service'; +import { PageNotFoundComponent } from './not-found.component'; +import { CanDeactivateGuard } from './can-deactivate-guard.service'; +import { AuthGuard } from './auth-guard.service'; const appRoutes: Routes = [ { @@ -25,7 +26,8 @@ const appRoutes: Routes = [ { path: 'crisis-center', loadChildren: 'app/crisis-center/crisis-center.module#CrisisCenterModule' - } + }, + { path: '**', component: PageNotFoundComponent } ]; @NgModule({ diff --git a/public/docs/_examples/router/ts/app/app-routing.module.ts b/public/docs/_examples/router/ts/app/app-routing.module.ts index 9cef4db94e..119d92aecd 100644 --- a/public/docs/_examples/router/ts/app/app-routing.module.ts +++ b/public/docs/_examples/router/ts/app/app-routing.module.ts @@ -3,8 +3,9 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { CanDeactivateGuard } from './can-deactivate-guard.service'; -import { AuthGuard } from './auth-guard.service'; +import { PageNotFoundComponent } from './not-found.component'; +import { CanDeactivateGuard } from './can-deactivate-guard.service'; +import { AuthGuard } from './auth-guard.service'; import { PreloadSelectedModules } from './selective-preload-strategy'; const appRoutes: Routes = [ @@ -25,6 +26,10 @@ const appRoutes: Routes = [ data: { preload: true } + }, + { + path: '**', + component: PageNotFoundComponent } // #enddocregion preload-v2 ]; diff --git a/public/docs/_examples/router/ts/app/app.module.1.ts b/public/docs/_examples/router/ts/app/app.module.1.ts index f98ef7528e..8f3126b660 100644 --- a/public/docs/_examples/router/ts/app/app.module.1.ts +++ b/public/docs/_examples/router/ts/app/app.module.1.ts @@ -1,23 +1,27 @@ // #docplaster // #docregion +// #docregion first-config import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { FormsModule } from '@angular/forms'; -// #docregion import-router, route-config +// #docregion import-router import { RouterModule, Routes } from '@angular/router'; -// #enddocregion import-router, route-config +// #enddocregion import-router -// #docregion router-basics import { AppComponent } from './app.component'; import { CrisisListComponent } from './crisis-list.component'; import { HeroListComponent } from './hero-list.component'; +// #enddocregion first-config +import { PageNotFoundComponent }from './not-found.component'; +// #docregion first-config -// #docregion route-config const appRoutes: Routes = [ { path: 'crisis-center', component: CrisisListComponent }, - { path: 'heroes', component: HeroListComponent } + { path: 'heroes', component: HeroListComponent }, +// #enddocregion first-config + { path: '**', component: PageNotFoundComponent } +// #docregion first-config ]; -// #enddocregion route-config @NgModule({ imports: [ @@ -28,11 +32,13 @@ const appRoutes: Routes = [ declarations: [ AppComponent, HeroListComponent, - CrisisListComponent + CrisisListComponent, +// #enddocregion first-config + PageNotFoundComponent +// #docregion first-config ], bootstrap: [ AppComponent ] }) -// #enddocregion router-basics export class AppModule { } // #enddocregion diff --git a/public/docs/_examples/router/ts/app/app.module.2.ts b/public/docs/_examples/router/ts/app/app.module.2.ts index 2d82802253..42e7e51de3 100644 --- a/public/docs/_examples/router/ts/app/app.module.2.ts +++ b/public/docs/_examples/router/ts/app/app.module.2.ts @@ -10,6 +10,7 @@ import { AppRoutingModule } from './app-routing.module'; import { CrisisListComponent } from './crisis-list.component'; import { HeroListComponent } from './hero-list.component'; +import { PageNotFoundComponent }from './not-found.component'; @NgModule({ imports: [ @@ -20,7 +21,8 @@ import { HeroListComponent } from './hero-list.component'; declarations: [ AppComponent, HeroListComponent, - CrisisListComponent + CrisisListComponent, + PageNotFoundComponent ], bootstrap: [ AppComponent ] }) diff --git a/public/docs/_examples/router/ts/app/app.module.3.ts b/public/docs/_examples/router/ts/app/app.module.3.ts index f7aeccae34..36cfeefe0e 100644 --- a/public/docs/_examples/router/ts/app/app.module.3.ts +++ b/public/docs/_examples/router/ts/app/app.module.3.ts @@ -10,7 +10,8 @@ import { AppRoutingModule } from './app-routing.module'; import { HeroesModule } from './heroes/heroes.module'; -import { CrisisListComponent } from './crisis-list.component'; +import { CrisisListComponent } from './crisis-list.component'; +import { PageNotFoundComponent } from './not-found.component'; @NgModule({ imports: [ @@ -21,7 +22,8 @@ import { CrisisListComponent } from './crisis-list.component'; ], declarations: [ AppComponent, - CrisisListComponent + CrisisListComponent, + PageNotFoundComponent ], bootstrap: [ AppComponent ] }) diff --git a/public/docs/_examples/router/ts/app/app.module.4.ts b/public/docs/_examples/router/ts/app/app.module.4.ts index f30dfccd00..9a51024775 100644 --- a/public/docs/_examples/router/ts/app/app.module.4.ts +++ b/public/docs/_examples/router/ts/app/app.module.4.ts @@ -6,6 +6,7 @@ import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; import { AppComponent } from './app.component'; +import { PageNotFoundComponent }from './not-found.component'; import { AppRoutingModule } from './app-routing.module'; import { HeroesModule } from './heroes/heroes.module'; @@ -30,7 +31,8 @@ import { DialogService } from './dialog.service'; AppRoutingModule ], declarations: [ - AppComponent + AppComponent, + PageNotFoundComponent ], providers: [ DialogService diff --git a/public/docs/_examples/router/ts/app/app.module.5.ts b/public/docs/_examples/router/ts/app/app.module.5.ts index 119d583578..c28656005b 100644 --- a/public/docs/_examples/router/ts/app/app.module.5.ts +++ b/public/docs/_examples/router/ts/app/app.module.5.ts @@ -3,8 +3,9 @@ import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { FormsModule } from '@angular/forms'; -import { AppComponent } from './app.component'; -import { AppRoutingModule } from './app-routing.module'; +import { AppComponent } from './app.component'; +import { PageNotFoundComponent }from './not-found.component'; +import { AppRoutingModule } from './app-routing.module'; import { HeroesModule } from './heroes/heroes.module'; import { CrisisCenterModule } from './crisis-center/crisis-center.module'; @@ -22,7 +23,8 @@ import { DialogService } from './dialog.service'; AppRoutingModule ], declarations: [ - AppComponent + AppComponent, + PageNotFoundComponent ], providers: [ DialogService diff --git a/public/docs/_examples/router/ts/app/app.module.7.ts b/public/docs/_examples/router/ts/app/app.module.7.ts index 8313e3aacf..e38d4dc597 100644 --- a/public/docs/_examples/router/ts/app/app.module.7.ts +++ b/public/docs/_examples/router/ts/app/app.module.7.ts @@ -3,8 +3,9 @@ import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { FormsModule } from '@angular/forms'; -import { AppComponent } from './app.component'; -import { AppRoutingModule } from './app-routing.module'; +import { AppComponent } from './app.component'; +import { PageNotFoundComponent }from './not-found.component'; +import { AppRoutingModule } from './app-routing.module'; import { HeroesModule } from './heroes/heroes.module'; import { CrisisCenterModule } from './crisis-center/crisis-center.module'; @@ -24,6 +25,7 @@ import { DialogService } from './dialog.service'; ], declarations: [ AppComponent, + PageNotFoundComponent, LoginComponent ], providers: [ diff --git a/public/docs/_examples/router/ts/app/app.module.ts b/public/docs/_examples/router/ts/app/app.module.ts index e557abfb30..7f710fad1f 100644 --- a/public/docs/_examples/router/ts/app/app.module.ts +++ b/public/docs/_examples/router/ts/app/app.module.ts @@ -4,6 +4,7 @@ import { BrowserModule } from '@angular/platform-browser'; import { FormsModule } from '@angular/forms'; import { AppComponent } from './app.component'; +import { PageNotFoundComponent } from './not-found.component'; import { AppRoutingModule } from './app-routing.module'; import { HeroesModule } from './heroes/heroes.module'; @@ -22,6 +23,7 @@ import { DialogService } from './dialog.service'; ], declarations: [ AppComponent, + PageNotFoundComponent, LoginComponent ], providers: [ diff --git a/public/docs/_examples/router/ts/app/hero-list.component.ts b/public/docs/_examples/router/ts/app/hero-list.component.ts index 5dbbe17d8e..6ca130c89e 100644 --- a/public/docs/_examples/router/ts/app/hero-list.component.ts +++ b/public/docs/_examples/router/ts/app/hero-list.component.ts @@ -5,6 +5,9 @@ import { Component } from '@angular/core'; @Component({ template: `
Get your heroes here
` +Get your heroes here
+ + + ` }) export class HeroListComponent { } diff --git a/public/docs/_examples/router/ts/app/heroes/hero-list.component.1.ts b/public/docs/_examples/router/ts/app/heroes/hero-list.component.1.ts index 878fef4694..37b129c0e7 100644 --- a/public/docs/_examples/router/ts/app/heroes/hero-list.component.1.ts +++ b/public/docs/_examples/router/ts/app/heroes/hero-list.component.1.ts @@ -16,6 +16,8 @@ import { Hero, HeroService } from './hero.service'; {{ hero.id }} {{ hero.name }} + + ` // #enddocregion template }) diff --git a/public/docs/_examples/router/ts/app/heroes/hero-list.component.ts b/public/docs/_examples/router/ts/app/heroes/hero-list.component.ts index ff0378a836..d4373a6e3a 100644 --- a/public/docs/_examples/router/ts/app/heroes/hero-list.component.ts +++ b/public/docs/_examples/router/ts/app/heroes/hero-list.component.ts @@ -23,6 +23,8 @@ import { Hero, HeroService } from './hero.service'; {{ hero.id }} {{ hero.name }} + + ` // #enddocregion template }) diff --git a/public/docs/ts/latest/guide/router.jade b/public/docs/ts/latest/guide/router.jade index c93af23469..f2495494d5 100644 --- a/public/docs/ts/latest/guide/router.jade +++ b/public/docs/ts/latest/guide/router.jade @@ -34,6 +34,7 @@ include ../../../_includes/_see-addr-bar * Setting the [base href](#base-href) * Importing from the [router library](#import) * [configuring the router](#route-config) + * handling unmatched URLs with a [wildcard route](#wildcard-route) * the [link parameters array](#link-parameters-array) that propels router navigation * navigating when the user clicks a data-bound [RouterLink](#router-link) * navigating under [program control](#navigate) @@ -104,6 +105,7 @@ include ../../../_includes/_see-addr-bar +makeExcerpt('app/app.module.0.ts (excerpt)', 'route-config') + .l-sub-section :marked The `RouterModule` is provided an array of *routes* that describe how to navigate. @@ -436,7 +438,7 @@ h4#define-routes Define routes unseen providers that the routing library requires. Once our application is bootstrapped, the `Router` will perform the initial navigation based on the current browser URL. -+makeExcerpt('app/app.module.1.ts') ++makeExcerpt('app/app.module.1.ts', 'first-config') .l-sub-section :marked @@ -515,6 +517,44 @@ h3#router-directives Router Directives +makeExcerpt('app/app.component.1.ts') +h3#wildcard-route Wildcard Routes +:marked + We've created two routes in our app so far, one to `/crisis-center` and the other to `/heroes`. We also + want to handle routes that don't exist in our current configuration. This protects us against users + entering invalid URLs, and makes sure we display an appropriate page in those situations. The `Router` + handles invalid routes by using a **wildcard** route, which is used as a catch-all route for any + routes not previously matched by a `path` in our route setup. + + A `wildcard` route is configured with a **path** consisting of two asterisks. The `Router` will only match + this route if no other specific route has been found first. Wildcard routes can also be used to + [redirect](#redirect) to an existing route. + +.l-sub-section + :marked + Wildcard routes are the least specific routes in the route configuration and should be included + last in the configuration, as the `Router` uses a [first match wins](#example-config) strategy. + +:marked + We'll add a `RouterLink` to our `/heroes` page that navigates `/sidekicks`. We have not + built our sidekicks page or route yet, but our `wildcard` route will catch any navigation + attempt to this page. + ++makeExcerpt('app/hero-list.component.ts') + +:marked + We'll create a simple `PageNotFoundComponent` to display when our users visit invalid URLs. + ++makeExcerpt('app/not-found.component.ts (404 component)', '') + +:marked + We'll add a wildcard route to our configuration to serve a 404 page for any invalid URLs entered. + As with our other components, we'll add the `PageNotFoundComponent` to our `AppModule` declarations. + ++makeExcerpt('app/app.module.1.ts') + +:marked + When the `/sidekicks` URL is visited, the browser URL will remain as `/sidekicks` but our 404 page will be displayed. + :marked ### "Getting Started" wrap-up @@ -526,7 +566,8 @@ h3#router-directives Router Directives * add a nav bar to the shell template with anchor tags, `routerLink` and `routerLinkActive` directives * add a `router-outlet` to the shell template where views will be displayed * configure the router module with `RouterModule.forRoot` - * set the router to compose "HTML 5" browser URLs. + * set the router to compose "HTML 5" browser URLs + * handle invalid routes with a `wildcard` route The rest of the starter app is mundane, with little interest from a router perspective. Here are the details for readers inclined to build the sample through to this milestone. @@ -541,6 +582,7 @@ h3#router-directives Router Directives .file app.module.ts .file crisis-list.component.ts .file hero-list.component.ts + .file not-found.component.ts .file main.ts .file node_modules ... .file index.html @@ -556,6 +598,7 @@ h3#router-directives Router Directives router/ts/app/main.ts, router/ts/app/hero-list.component.ts, router/ts/app/crisis-list.component.ts, + router/ts/app/not-found.component.ts, router/ts/index.html`, ',,,,', `app.component.ts, @@ -563,6 +606,7 @@ h3#router-directives Router Directives main.ts, hero-list.component.ts, crisis-list.component.ts, + not-found.component.ts, index.html`) .l-main-section#routing-module