diff --git a/aio/content/examples/router/e2e/src/app.e2e-spec.ts b/aio/content/examples/router/e2e/src/app.e2e-spec.ts index 89adb2efab..41d3a49c9a 100644 --- a/aio/content/examples/router/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/router/e2e/src/app.e2e-spec.ts @@ -4,7 +4,7 @@ import { browser, element, by, ExpectedConditions } from 'protractor'; const numDashboardTabs = 5; const numCrises = 4; -const numHeroes = 6; +const numHeroes = 10; const EC = ExpectedConditions; describe('Router', () => { @@ -13,34 +13,34 @@ describe('Router', () => { function getPageStruct() { const hrefEles = element.all(by.css('app-root > nav a')); - const crisisDetail = element.all(by.css('app-root > div > ng-component > ng-component > ng-component > div')).first(); - const heroDetail = element(by.css('app-root > div > ng-component')); + const crisisDetail = element.all(by.css('app-root > div > app-crisis-center > app-crisis-list > app-crisis-detail > div')).first(); + const heroDetail = element(by.css('app-root > div > app-hero-detail')); return { hrefs: hrefEles, activeHref: element(by.css('app-root > nav a.active')), crisisHref: hrefEles.get(0), - crisisList: element.all(by.css('app-root > div > ng-component > ng-component li')), + crisisList: element.all(by.css('app-root > div > app-crisis-center > app-crisis-list li')), crisisDetail: crisisDetail, crisisDetailTitle: crisisDetail.element(by.xpath('*[1]')), heroesHref: hrefEles.get(1), - heroesList: element.all(by.css('app-root > div > ng-component li')), + heroesList: element.all(by.css('app-root > div > app-hero-list li')), heroDetail: heroDetail, heroDetailTitle: heroDetail.element(by.xpath('*[2]')), adminHref: hrefEles.get(2), - adminPreloadList: element.all(by.css('app-root > div > ng-component > ng-component > ul > li')), + adminPreloadList: element.all(by.css('app-root > div > app-admin > app-admin-dashboard > ul > li')), loginHref: hrefEles.get(3), - loginButton: element.all(by.css('app-root > div > ng-component > p > button')), + loginButton: element.all(by.css('app-root > div > app-login > p > button')), contactHref: hrefEles.get(4), contactCancelButton: element.all(by.buttonText('Cancel')), - primaryOutlet: element.all(by.css('app-root > div > ng-component')), - secondaryOutlet: element.all(by.css('app-root > ng-component')) + primaryOutlet: element.all(by.css('app-root > div > app-hero-list')), + secondaryOutlet: element.all(by.css('app-root > app-compose-message')) }; } diff --git a/aio/content/examples/router/src/app/admin/admin-dashboard.component.1.ts b/aio/content/examples/router/src/app/admin/admin-dashboard.component.1.ts deleted file mode 100644 index ffa3e3cb8f..0000000000 --- a/aio/content/examples/router/src/app/admin/admin-dashboard.component.1.ts +++ /dev/null @@ -1,9 +0,0 @@ -// #docregion -import { Component } from '@angular/core'; - -@Component({ - template: ` -

Dashboard

- ` -}) -export class AdminDashboardComponent { } diff --git a/aio/content/examples/router/src/app/admin/admin-dashboard/admin-dashboard.component.1.html b/aio/content/examples/router/src/app/admin/admin-dashboard/admin-dashboard.component.1.html new file mode 100644 index 0000000000..754a262154 --- /dev/null +++ b/aio/content/examples/router/src/app/admin/admin-dashboard/admin-dashboard.component.1.html @@ -0,0 +1 @@ +

Dashboard

\ No newline at end of file diff --git a/aio/content/examples/router/src/app/admin/admin-dashboard.component.2.ts b/aio/content/examples/router/src/app/admin/admin-dashboard/admin-dashboard.component.1.ts similarity index 82% rename from aio/content/examples/router/src/app/admin/admin-dashboard.component.2.ts rename to aio/content/examples/router/src/app/admin/admin-dashboard/admin-dashboard.component.1.ts index 19406bea9b..8806d7cea8 100644 --- a/aio/content/examples/router/src/app/admin/admin-dashboard.component.2.ts +++ b/aio/content/examples/router/src/app/admin/admin-dashboard/admin-dashboard.component.1.ts @@ -5,13 +5,9 @@ import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; @Component({ - template: ` -

Dashboard

- -

Session ID: {{ sessionId | async }}

- -

Token: {{ token | async }}

- ` + selector: 'app-admin-dashboard', + templateUrl: './admin-dashboard.component.html', + styleUrls: ['./admin-dashboard.component.css'] }) export class AdminDashboardComponent implements OnInit { sessionId: Observable; diff --git a/aio/content/examples/router/src/app/admin/admin-dashboard/admin-dashboard.component.2.html b/aio/content/examples/router/src/app/admin/admin-dashboard/admin-dashboard.component.2.html new file mode 100644 index 0000000000..9fedba9793 --- /dev/null +++ b/aio/content/examples/router/src/app/admin/admin-dashboard/admin-dashboard.component.2.html @@ -0,0 +1,5 @@ +

Dashboard

+ +

Session ID: {{ sessionId | async }}

+ +

Token: {{ token | async }}

\ No newline at end of file diff --git a/aio/content/examples/router/src/app/admin/admin-dashboard/admin-dashboard.component.css b/aio/content/examples/router/src/app/admin/admin-dashboard/admin-dashboard.component.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/aio/content/examples/router/src/app/admin/admin-dashboard/admin-dashboard.component.html b/aio/content/examples/router/src/app/admin/admin-dashboard/admin-dashboard.component.html new file mode 100644 index 0000000000..9c14d5b266 --- /dev/null +++ b/aio/content/examples/router/src/app/admin/admin-dashboard/admin-dashboard.component.html @@ -0,0 +1,10 @@ +

Dashboard

+ +

Session ID: {{ sessionId | async }}

+ +

Token: {{ token | async }}

+ +Preloaded Modules + \ No newline at end of file diff --git a/aio/content/examples/router/src/app/admin/admin-dashboard.component.ts b/aio/content/examples/router/src/app/admin/admin-dashboard/admin-dashboard.component.ts similarity index 67% rename from aio/content/examples/router/src/app/admin/admin-dashboard.component.ts rename to aio/content/examples/router/src/app/admin/admin-dashboard/admin-dashboard.component.ts index 11be8fd029..2447f3ad9c 100644 --- a/aio/content/examples/router/src/app/admin/admin-dashboard.component.ts +++ b/aio/content/examples/router/src/app/admin/admin-dashboard/admin-dashboard.component.ts @@ -4,22 +4,12 @@ import { ActivatedRoute } from '@angular/router'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; -import { SelectivePreloadingStrategy } from '../selective-preloading-strategy'; - +import { SelectivePreloadingStrategyService } from '../../selective-preloading-strategy.service'; @Component({ - template: ` -

Dashboard

- -

Session ID: {{ sessionId | async }}

- -

Token: {{ token | async }}

- - Preloaded Modules - - ` + selector: 'app-admin-dashboard', + templateUrl: './admin-dashboard.component.html', + styleUrls: ['./admin-dashboard.component.css'] }) export class AdminDashboardComponent implements OnInit { sessionId: Observable; @@ -28,7 +18,7 @@ export class AdminDashboardComponent implements OnInit { constructor( private route: ActivatedRoute, - private preloadStrategy: SelectivePreloadingStrategy + preloadStrategy: SelectivePreloadingStrategyService ) { this.modules = preloadStrategy.preloadedModules; } diff --git a/aio/content/examples/router/src/app/admin/admin-routing.module.1.ts b/aio/content/examples/router/src/app/admin/admin-routing.module.1.ts index e7d83f113f..6c3f6cbfc4 100644 --- a/aio/content/examples/router/src/app/admin/admin-routing.module.1.ts +++ b/aio/content/examples/router/src/app/admin/admin-routing.module.1.ts @@ -3,10 +3,10 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { AdminComponent } from './admin.component'; -import { AdminDashboardComponent } from './admin-dashboard.component'; -import { ManageCrisesComponent } from './manage-crises.component'; -import { ManageHeroesComponent } from './manage-heroes.component'; +import { AdminComponent } from './admin/admin.component'; +import { AdminDashboardComponent } from './admin-dashboard/admin-dashboard.component'; +import { ManageCrisesComponent } from './manage-crises/manage-crises.component'; +import { ManageHeroesComponent } from './manage-heroes/manage-heroes.component'; // #docregion admin-routes const adminRoutes: Routes = [ diff --git a/aio/content/examples/router/src/app/admin/admin-routing.module.2.ts b/aio/content/examples/router/src/app/admin/admin-routing.module.2.ts index d945201afe..9b80c24390 100644 --- a/aio/content/examples/router/src/app/admin/admin-routing.module.2.ts +++ b/aio/content/examples/router/src/app/admin/admin-routing.module.2.ts @@ -3,13 +3,13 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { AdminComponent } from './admin.component'; -import { AdminDashboardComponent } from './admin-dashboard.component'; -import { ManageCrisesComponent } from './manage-crises.component'; -import { ManageHeroesComponent } from './manage-heroes.component'; +import { AdminComponent } from './admin/admin.component'; +import { AdminDashboardComponent } from './admin-dashboard/admin-dashboard.component'; +import { ManageCrisesComponent } from './manage-crises/manage-crises.component'; +import { ManageHeroesComponent } from './manage-heroes/manage-heroes.component'; // #docregion admin-route -import { AuthGuard } from '../auth-guard.service'; +import { AuthGuard } from '../auth/auth.guard'; const adminRoutes: Routes = [ { diff --git a/aio/content/examples/router/src/app/admin/admin-routing.module.3.ts b/aio/content/examples/router/src/app/admin/admin-routing.module.3.ts index 63f1c9aaf4..b337cab0b5 100644 --- a/aio/content/examples/router/src/app/admin/admin-routing.module.3.ts +++ b/aio/content/examples/router/src/app/admin/admin-routing.module.3.ts @@ -3,13 +3,13 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { AdminComponent } from './admin.component'; -import { AdminDashboardComponent } from './admin-dashboard.component'; -import { ManageCrisesComponent } from './manage-crises.component'; -import { ManageHeroesComponent } from './manage-heroes.component'; +import { AdminComponent } from './admin/admin.component'; +import { AdminDashboardComponent } from './admin-dashboard/admin-dashboard.component'; +import { ManageCrisesComponent } from './manage-crises/manage-crises.component'; +import { ManageHeroesComponent } from './manage-heroes/manage-heroes.component'; // #docregion admin-route -import { AuthGuard } from '../auth-guard.service'; +import { AuthGuard } from '../auth/auth.guard'; // #docregion can-activate-child const adminRoutes: Routes = [ diff --git a/aio/content/examples/router/src/app/admin/admin-routing.module.ts b/aio/content/examples/router/src/app/admin/admin-routing.module.ts index 2b1048d110..6faa557b16 100644 --- a/aio/content/examples/router/src/app/admin/admin-routing.module.ts +++ b/aio/content/examples/router/src/app/admin/admin-routing.module.ts @@ -3,12 +3,12 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { AdminComponent } from './admin.component'; -import { AdminDashboardComponent } from './admin-dashboard.component'; -import { ManageCrisesComponent } from './manage-crises.component'; -import { ManageHeroesComponent } from './manage-heroes.component'; +import { AdminComponent } from './admin/admin.component'; +import { AdminDashboardComponent } from './admin-dashboard/admin-dashboard.component'; +import { ManageCrisesComponent } from './manage-crises/manage-crises.component'; +import { ManageHeroesComponent } from './manage-heroes/manage-heroes.component'; -import { AuthGuard } from '../auth-guard.service'; +import { AuthGuard } from '../auth/auth.guard'; const adminRoutes: Routes = [ { diff --git a/aio/content/examples/router/src/app/admin/admin.component.ts b/aio/content/examples/router/src/app/admin/admin.component.ts deleted file mode 100644 index 30abfa4524..0000000000 --- a/aio/content/examples/router/src/app/admin/admin.component.ts +++ /dev/null @@ -1,17 +0,0 @@ -// #docregion -import { Component } from '@angular/core'; - -@Component({ - template: ` -

ADMIN

- - - ` -}) -export class AdminComponent { -} diff --git a/aio/content/examples/router/src/app/admin/admin.module.ts b/aio/content/examples/router/src/app/admin/admin.module.ts index 2736f00e1d..d276b0efd5 100644 --- a/aio/content/examples/router/src/app/admin/admin.module.ts +++ b/aio/content/examples/router/src/app/admin/admin.module.ts @@ -2,10 +2,10 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { AdminComponent } from './admin.component'; -import { AdminDashboardComponent } from './admin-dashboard.component'; -import { ManageCrisesComponent } from './manage-crises.component'; -import { ManageHeroesComponent } from './manage-heroes.component'; +import { AdminComponent } from './admin/admin.component'; +import { AdminDashboardComponent } from './admin-dashboard/admin-dashboard.component'; +import { ManageCrisesComponent } from './manage-crises/manage-crises.component'; +import { ManageHeroesComponent } from './manage-heroes/manage-heroes.component'; import { AdminRoutingModule } from './admin-routing.module'; diff --git a/aio/content/examples/router/src/app/admin/admin/admin.component.css b/aio/content/examples/router/src/app/admin/admin/admin.component.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/aio/content/examples/router/src/app/admin/admin/admin.component.html b/aio/content/examples/router/src/app/admin/admin/admin.component.html new file mode 100644 index 0000000000..6e924c35d6 --- /dev/null +++ b/aio/content/examples/router/src/app/admin/admin/admin.component.html @@ -0,0 +1,8 @@ +

ADMIN

+ + \ No newline at end of file diff --git a/aio/content/examples/router/src/app/admin/admin/admin.component.ts b/aio/content/examples/router/src/app/admin/admin/admin.component.ts new file mode 100644 index 0000000000..beb6b89fcf --- /dev/null +++ b/aio/content/examples/router/src/app/admin/admin/admin.component.ts @@ -0,0 +1,10 @@ +// #docregion +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-admin', + templateUrl: './admin.component.html', + styleUrls: ['./admin.component.css'] +}) +export class AdminComponent { +} diff --git a/aio/content/examples/router/src/app/admin/manage-crises.component.ts b/aio/content/examples/router/src/app/admin/manage-crises.component.ts deleted file mode 100644 index d3176563eb..0000000000 --- a/aio/content/examples/router/src/app/admin/manage-crises.component.ts +++ /dev/null @@ -1,9 +0,0 @@ -// #docregion -import { Component } from '@angular/core'; - -@Component({ - template: ` -

Manage your crises here

- ` -}) -export class ManageCrisesComponent { } diff --git a/aio/content/examples/router/src/app/admin/manage-crises/manage-crises.component.css b/aio/content/examples/router/src/app/admin/manage-crises/manage-crises.component.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/aio/content/examples/router/src/app/admin/manage-crises/manage-crises.component.html b/aio/content/examples/router/src/app/admin/manage-crises/manage-crises.component.html new file mode 100644 index 0000000000..4edfa72133 --- /dev/null +++ b/aio/content/examples/router/src/app/admin/manage-crises/manage-crises.component.html @@ -0,0 +1 @@ +

Manage your crises here

\ No newline at end of file diff --git a/aio/content/examples/router/src/app/admin/manage-crises/manage-crises.component.ts b/aio/content/examples/router/src/app/admin/manage-crises/manage-crises.component.ts new file mode 100644 index 0000000000..c3fe3bb92d --- /dev/null +++ b/aio/content/examples/router/src/app/admin/manage-crises/manage-crises.component.ts @@ -0,0 +1,9 @@ +// #docregion +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-manage-crises', + templateUrl: './manage-crises.component.html', + styleUrls: ['./manage-crises.component.css'] +}) +export class ManageCrisesComponent { } diff --git a/aio/content/examples/router/src/app/admin/manage-heroes.component.ts b/aio/content/examples/router/src/app/admin/manage-heroes.component.ts deleted file mode 100644 index 7f3a39893d..0000000000 --- a/aio/content/examples/router/src/app/admin/manage-heroes.component.ts +++ /dev/null @@ -1,9 +0,0 @@ -// #docregion -import { Component } from '@angular/core'; - -@Component({ - template: ` -

Manage your heroes here

- ` -}) -export class ManageHeroesComponent { } diff --git a/aio/content/examples/router/src/app/admin/manage-heroes/manage-heroes.component.css b/aio/content/examples/router/src/app/admin/manage-heroes/manage-heroes.component.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/aio/content/examples/router/src/app/admin/manage-heroes/manage-heroes.component.html b/aio/content/examples/router/src/app/admin/manage-heroes/manage-heroes.component.html new file mode 100644 index 0000000000..3e5256527d --- /dev/null +++ b/aio/content/examples/router/src/app/admin/manage-heroes/manage-heroes.component.html @@ -0,0 +1 @@ +

Manage your heroes here

\ No newline at end of file diff --git a/aio/content/examples/router/src/app/admin/manage-heroes/manage-heroes.component.ts b/aio/content/examples/router/src/app/admin/manage-heroes/manage-heroes.component.ts new file mode 100644 index 0000000000..cb68fb3711 --- /dev/null +++ b/aio/content/examples/router/src/app/admin/manage-heroes/manage-heroes.component.ts @@ -0,0 +1,9 @@ +// #docregion +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-manage-hereos', + templateUrl: './manage-heroes.component.html', + styleUrls: ['./manage-heroes.component.css'] +}) +export class ManageHeroesComponent { } diff --git a/aio/content/examples/router/src/app/app-routing.module.1.ts b/aio/content/examples/router/src/app/app-routing.module.1.ts index 436291f499..181384575f 100644 --- a/aio/content/examples/router/src/app/app-routing.module.1.ts +++ b/aio/content/examples/router/src/app/app-routing.module.1.ts @@ -2,9 +2,9 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { CrisisListComponent } from './crisis-list.component'; -import { HeroListComponent } from './hero-list.component'; -import { PageNotFoundComponent } from './not-found.component'; +import { CrisisListComponent } from './crisis-list/crisis-list.component'; +import { HeroListComponent } from './hero-list/hero-list.component'; +import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; // #docregion appRoutes const appRoutes: Routes = [ diff --git a/aio/content/examples/router/src/app/app-routing.module.2.ts b/aio/content/examples/router/src/app/app-routing.module.2.ts index c4df89d7f8..01192901f6 100644 --- a/aio/content/examples/router/src/app/app-routing.module.2.ts +++ b/aio/content/examples/router/src/app/app-routing.module.2.ts @@ -1,14 +1,19 @@ // #docregion +// #docregion milestone3 import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { CrisisListComponent } from './crisis-list.component'; -// import { HeroListComponent } from './hero-list.component'; // <-- delete this line -import { PageNotFoundComponent } from './not-found.component'; +import { CrisisListComponent } from './crisis-list/crisis-list.component'; +// #enddocregion milestone3 +// import { HeroListComponent } from './hero-list/hero-list.component'; // <-- delete this line +// #docregion milestone3 +import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; const appRoutes: Routes = [ { path: 'crisis-center', component: CrisisListComponent }, +// #enddocregion milestone3 // { path: 'heroes', component: HeroListComponent }, // <-- delete this line +// #docregion milestone3 { path: '', redirectTo: '/heroes', pathMatch: 'full' }, { path: '**', component: PageNotFoundComponent } ]; @@ -25,3 +30,4 @@ const appRoutes: Routes = [ ] }) export class AppRoutingModule {} +// #enddocregion milestone3 diff --git a/aio/content/examples/router/src/app/app-routing.module.3.ts b/aio/content/examples/router/src/app/app-routing.module.3.ts index 354ca6e740..4351476a4e 100644 --- a/aio/content/examples/router/src/app/app-routing.module.3.ts +++ b/aio/content/examples/router/src/app/app-routing.module.3.ts @@ -3,8 +3,10 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { ComposeMessageComponent } from './compose-message.component'; -import { PageNotFoundComponent } from './not-found.component'; +// #enddocregion v3 +import { ComposeMessageComponent } from './compose-message/compose-message.component'; +// #docregion v3 +import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; const appRoutes: Routes = [ // #enddocregion v3 diff --git a/aio/content/examples/router/src/app/app-routing.module.4.ts b/aio/content/examples/router/src/app/app-routing.module.4.ts index 9c0d7bb928..5943ad7d05 100644 --- a/aio/content/examples/router/src/app/app-routing.module.4.ts +++ b/aio/content/examples/router/src/app/app-routing.module.4.ts @@ -2,9 +2,9 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { ComposeMessageComponent } from './compose-message.component'; -import { CanDeactivateGuard } from './can-deactivate-guard.service'; -import { PageNotFoundComponent } from './not-found.component'; +import { ComposeMessageComponent } from './compose-message/compose-message.component'; +import { CanDeactivateGuard } from './can-deactivate.guard'; +import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; const appRoutes: Routes = [ { @@ -25,9 +25,6 @@ const appRoutes: Routes = [ ], exports: [ RouterModule - ], - providers: [ - CanDeactivateGuard ] }) export class AppRoutingModule {} diff --git a/aio/content/examples/router/src/app/app-routing.module.5.ts b/aio/content/examples/router/src/app/app-routing.module.5.ts index a12bd2cc7e..a120771875 100644 --- a/aio/content/examples/router/src/app/app-routing.module.5.ts +++ b/aio/content/examples/router/src/app/app-routing.module.5.ts @@ -5,11 +5,10 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; // #enddocregion import-router -import { ComposeMessageComponent } from './compose-message.component'; -import { PageNotFoundComponent } from './not-found.component'; +import { ComposeMessageComponent } from './compose-message/compose-message.component'; +import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; -import { CanDeactivateGuard } from './can-deactivate-guard.service'; -import { AuthGuard } from './auth-guard.service'; +import { AuthGuard } from './auth/auth.guard'; const appRoutes: Routes = [ @@ -21,7 +20,7 @@ const appRoutes: Routes = [ // #docregion admin, admin-1 { path: 'admin', - loadChildren: 'app/admin/admin.module#AdminModule', + loadChildren: './admin/admin.module#AdminModule', // #enddocregion admin-1 canLoad: [AuthGuard] // #docregion admin-1 @@ -40,9 +39,6 @@ const appRoutes: Routes = [ ], exports: [ RouterModule - ], - providers: [ - CanDeactivateGuard ] }) export class AppRoutingModule {} diff --git a/aio/content/examples/router/src/app/app-routing.module.6.ts b/aio/content/examples/router/src/app/app-routing.module.6.ts index 83a6ab3521..e1c81f2498 100644 --- a/aio/content/examples/router/src/app/app-routing.module.6.ts +++ b/aio/content/examples/router/src/app/app-routing.module.6.ts @@ -8,11 +8,10 @@ import { // #docregion preload-v1 } from '@angular/router'; -import { ComposeMessageComponent } from './compose-message.component'; -import { PageNotFoundComponent } from './not-found.component'; +import { ComposeMessageComponent } from './compose-message/compose-message.component'; +import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; -import { CanDeactivateGuard } from './can-deactivate-guard.service'; -import { AuthGuard } from './auth-guard.service'; +import { AuthGuard } from './auth/auth.guard'; const appRoutes: Routes = [ { @@ -22,12 +21,12 @@ const appRoutes: Routes = [ }, { path: 'admin', - loadChildren: 'app/admin/admin.module#AdminModule', + loadChildren: './admin/admin.module#AdminModule', canLoad: [AuthGuard] }, { path: 'crisis-center', - loadChildren: 'app/crisis-center/crisis-center.module#CrisisCenterModule' + loadChildren: './crisis-center/crisis-center.module#CrisisCenterModule' }, { path: '', redirectTo: '/heroes', pathMatch: 'full' }, { path: '**', component: PageNotFoundComponent } @@ -49,9 +48,6 @@ const appRoutes: Routes = [ ], exports: [ RouterModule - ], - providers: [ - CanDeactivateGuard ] }) export class AppRoutingModule {} diff --git a/aio/content/examples/router/src/app/app-routing.module.ts b/aio/content/examples/router/src/app/app-routing.module.ts index be5dd1d5c9..ffad588287 100644 --- a/aio/content/examples/router/src/app/app-routing.module.ts +++ b/aio/content/examples/router/src/app/app-routing.module.ts @@ -3,12 +3,11 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { ComposeMessageComponent } from './compose-message.component'; -import { PageNotFoundComponent } from './not-found.component'; +import { ComposeMessageComponent } from './compose-message/compose-message.component'; +import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; -import { CanDeactivateGuard } from './can-deactivate-guard.service'; -import { AuthGuard } from './auth-guard.service'; -import { SelectivePreloadingStrategy } from './selective-preloading-strategy'; +import { AuthGuard } from './auth/auth.guard'; +import { SelectivePreloadingStrategyService } from './selective-preloading-strategy.service'; const appRoutes: Routes = [ { @@ -18,13 +17,13 @@ const appRoutes: Routes = [ }, { path: 'admin', - loadChildren: 'app/admin/admin.module#AdminModule', + loadChildren: './admin/admin.module#AdminModule', canLoad: [AuthGuard] }, // #docregion preload-v2 { path: 'crisis-center', - loadChildren: 'app/crisis-center/crisis-center.module#CrisisCenterModule', + loadChildren: './crisis-center/crisis-center.module#CrisisCenterModule', data: { preload: true } }, // #enddocregion preload-v2 @@ -37,18 +36,13 @@ const appRoutes: Routes = [ RouterModule.forRoot( appRoutes, { - enableTracing: true, // <-- debugging purposes only - preloadingStrategy: SelectivePreloadingStrategy, - + enableTracing: false, // <-- debugging purposes only + preloadingStrategy: SelectivePreloadingStrategyService, } ) ], exports: [ RouterModule - ], - providers: [ - CanDeactivateGuard, - SelectivePreloadingStrategy ] }) export class AppRoutingModule { } diff --git a/aio/content/examples/router/src/app/app.component.1.html b/aio/content/examples/router/src/app/app.component.1.html new file mode 100644 index 0000000000..255232483e --- /dev/null +++ b/aio/content/examples/router/src/app/app.component.1.html @@ -0,0 +1,7 @@ + +

Angular Router

+ + \ No newline at end of file diff --git a/aio/content/examples/router/src/app/app.component.1.ts b/aio/content/examples/router/src/app/app.component.1.ts index c6fe6d2a77..68dc114661 100644 --- a/aio/content/examples/router/src/app/app.component.1.ts +++ b/aio/content/examples/router/src/app/app.component.1.ts @@ -1,18 +1,9 @@ -/* First version */ // #docregion import { Component } from '@angular/core'; @Component({ selector: 'app-root', - // #docregion template - template: ` -

Angular Router

- - - ` - // #enddocregion template + templateUrl: 'app.component.html', + styleUrls: ['app.component.css'] }) export class AppComponent { } diff --git a/aio/content/examples/router/src/app/app.component.2.html b/aio/content/examples/router/src/app/app.component.2.html new file mode 100644 index 0000000000..51e62b5bd1 --- /dev/null +++ b/aio/content/examples/router/src/app/app.component.2.html @@ -0,0 +1,9 @@ + +

Angular Router

+ +
+ +
\ No newline at end of file diff --git a/aio/content/examples/router/src/app/app.component.2.ts b/aio/content/examples/router/src/app/app.component.2.ts index 2e56506a58..79b5689cc8 100644 --- a/aio/content/examples/router/src/app/app.component.2.ts +++ b/aio/content/examples/router/src/app/app.component.2.ts @@ -4,24 +4,14 @@ import { Component } from '@angular/core'; // #docregion animation-imports import { RouterOutlet } from '@angular/router'; import { slideInAnimation } from './animations'; -// #enddocregion animation-imports @Component({ selector: 'app-root', - // #docregion template - template: ` -

Angular Router

- -
- -
- `, + templateUrl: 'app.component.html', + styleUrls: ['app.component.css'], animations: [ slideInAnimation ] - // #enddocregion template }) +// #enddocregion animation-imports // #docregion function-binding export class AppComponent { getAnimationData(outlet: RouterOutlet) { diff --git a/aio/content/examples/router/src/app/app.component.4.html b/aio/content/examples/router/src/app/app.component.4.html new file mode 100644 index 0000000000..4a32aedb34 --- /dev/null +++ b/aio/content/examples/router/src/app/app.component.4.html @@ -0,0 +1,15 @@ + +

Angular Router

+ + +
+ +
+ + \ No newline at end of file diff --git a/aio/content/examples/router/src/app/app.component.4.ts b/aio/content/examples/router/src/app/app.component.4.ts deleted file mode 100644 index 03a987ab37..0000000000 --- a/aio/content/examples/router/src/app/app.component.4.ts +++ /dev/null @@ -1,25 +0,0 @@ -// #docregion -import { Component } from '@angular/core'; - -@Component({ - selector: 'app-root', - // #docregion template - template: ` -

Angular Router

- - // #docregion outlets -
- -
- - // #enddocregion outlets - ` - // #enddocregion template -}) -export class AppComponent { } diff --git a/aio/content/examples/router/src/app/app.component.5.html b/aio/content/examples/router/src/app/app.component.5.html new file mode 100644 index 0000000000..4cc8c91ffc --- /dev/null +++ b/aio/content/examples/router/src/app/app.component.5.html @@ -0,0 +1,12 @@ + +

Angular Router

+ +
+ +
+ \ No newline at end of file diff --git a/aio/content/examples/router/src/app/app.component.5.ts b/aio/content/examples/router/src/app/app.component.5.ts deleted file mode 100644 index 61b711834e..0000000000 --- a/aio/content/examples/router/src/app/app.component.5.ts +++ /dev/null @@ -1,22 +0,0 @@ -// #docregion -import { Component } from '@angular/core'; - -@Component({ - selector: 'app-root', - // #docregion template - template: ` -

Angular Router

- -
- -
- - ` - // #enddocregion template -}) -export class AppComponent { } diff --git a/aio/content/examples/router/src/app/app.component.6.html b/aio/content/examples/router/src/app/app.component.6.html new file mode 100644 index 0000000000..b082558ecb --- /dev/null +++ b/aio/content/examples/router/src/app/app.component.6.html @@ -0,0 +1,13 @@ + +

Angular Router

+ +
+ +
+ \ No newline at end of file diff --git a/aio/content/examples/router/src/app/app.component.6.ts b/aio/content/examples/router/src/app/app.component.6.ts deleted file mode 100644 index 5543507d77..0000000000 --- a/aio/content/examples/router/src/app/app.component.6.ts +++ /dev/null @@ -1,25 +0,0 @@ -// #docplaster -// #docregion -import { Component } from '@angular/core'; - -@Component({ - selector: 'app-root', - // #docregion template - template: ` -

Angular Router

- -
- -
- - ` - // #enddocregion template -}) -export class AppComponent { -} diff --git a/aio/content/examples/router/src/app/app.component.css b/aio/content/examples/router/src/app/app.component.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/aio/content/examples/router/src/app/app.component.html b/aio/content/examples/router/src/app/app.component.html new file mode 100644 index 0000000000..70e0c7aeb2 --- /dev/null +++ b/aio/content/examples/router/src/app/app.component.html @@ -0,0 +1,13 @@ + +

Angular Router

+ +
+ +
+ \ No newline at end of file diff --git a/aio/content/examples/router/src/app/app.component.ts b/aio/content/examples/router/src/app/app.component.ts index 55b4cf9ec1..6747d50278 100644 --- a/aio/content/examples/router/src/app/app.component.ts +++ b/aio/content/examples/router/src/app/app.component.ts @@ -6,23 +6,9 @@ import { slideInAnimation } from './animations'; @Component({ selector: 'app-root', - // #docregion template - template: ` -

Angular Router

- -
- -
- - `, + templateUrl: 'app.component.html', + styleUrls: ['app.component.css'], animations: [ slideInAnimation ] - // #enddocregion template }) export class AppComponent { getAnimationData(outlet: RouterOutlet) { diff --git a/aio/content/examples/router/src/app/app.module.0.ts b/aio/content/examples/router/src/app/app.module.0.ts index 5d14073b1b..ca3c93cb6b 100644 --- a/aio/content/examples/router/src/app/app.module.0.ts +++ b/aio/content/examples/router/src/app/app.module.0.ts @@ -3,10 +3,10 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { HeroListComponent } from './hero-list.component'; -import { CrisisListComponent } from './crisis-list.component'; -import { PageNotFoundComponent } from './not-found.component'; -import { PageNotFoundComponent as HeroDetailComponent } from './not-found.component'; +import { HeroListComponent } from './hero-list/hero-list.component'; +import { CrisisListComponent } from './crisis-list/crisis-list.component'; +import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; +import { PageNotFoundComponent as HeroDetailComponent } from './page-not-found/page-not-found.component'; // #docregion const appRoutes: Routes = [ diff --git a/aio/content/examples/router/src/app/app.module.1.ts b/aio/content/examples/router/src/app/app.module.1.ts index e83e299a16..42a1b546fc 100644 --- a/aio/content/examples/router/src/app/app.module.1.ts +++ b/aio/content/examples/router/src/app/app.module.1.ts @@ -9,10 +9,10 @@ import { RouterModule, Routes } from '@angular/router'; // #enddocregion import-router import { AppComponent } from './app.component'; -import { CrisisListComponent } from './crisis-list.component'; -import { HeroListComponent } from './hero-list.component'; +import { CrisisListComponent } from './crisis-list/crisis-list.component'; +import { HeroListComponent } from './hero-list/hero-list.component'; // #enddocregion first-config -import { PageNotFoundComponent } from './not-found.component'; +import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; // #docregion first-config // #docregion appRoutes diff --git a/aio/content/examples/router/src/app/app.module.2.ts b/aio/content/examples/router/src/app/app.module.2.ts index 2ba739168c..5bb925b76c 100644 --- a/aio/content/examples/router/src/app/app.module.2.ts +++ b/aio/content/examples/router/src/app/app.module.2.ts @@ -8,9 +8,9 @@ import { FormsModule } from '@angular/forms'; import { AppComponent } from './app.component'; import { AppRoutingModule } from './app-routing.module'; -import { CrisisListComponent } from './crisis-list.component'; -import { HeroListComponent } from './hero-list.component'; -import { PageNotFoundComponent } from './not-found.component'; +import { CrisisListComponent } from './crisis-list/crisis-list.component'; +import { HeroListComponent } from './hero-list/hero-list.component'; +import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; @NgModule({ imports: [ diff --git a/aio/content/examples/router/src/app/app.module.3.ts b/aio/content/examples/router/src/app/app.module.3.ts index 862faf1c51..3277a6ded6 100644 --- a/aio/content/examples/router/src/app/app.module.3.ts +++ b/aio/content/examples/router/src/app/app.module.3.ts @@ -7,8 +7,8 @@ import { AppComponent } from './app.component'; import { AppRoutingModule } from './app-routing.module'; import { HeroesModule } from './heroes/heroes.module'; -import { CrisisListComponent } from './crisis-list.component'; -import { PageNotFoundComponent } from './not-found.component'; +import { CrisisListComponent } from './crisis-list/crisis-list.component'; +import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; @NgModule({ // #docregion module-imports @@ -27,3 +27,15 @@ import { PageNotFoundComponent } from './not-found.component'; bootstrap: [ AppComponent ] }) export class AppModule { } +// #enddocregion + +/* +// #docregion module-imports-2 + imports: [ + RouterModule.forChild([ + // Heroes Routes + ]), + AppRoutingModule + ], +// #enddocregion module-imports-2 +*/ diff --git a/aio/content/examples/router/src/app/app.module.4.ts b/aio/content/examples/router/src/app/app.module.4.ts index 4825572361..104a352e5d 100644 --- a/aio/content/examples/router/src/app/app.module.4.ts +++ b/aio/content/examples/router/src/app/app.module.4.ts @@ -6,19 +6,17 @@ import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; import { AppComponent } from './app.component'; -import { PageNotFoundComponent } from './not-found.component'; +import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; +import { ComposeMessageComponent } from './compose-message/compose-message.component'; import { AppRoutingModule } from './app-routing.module'; import { HeroesModule } from './heroes/heroes.module'; import { CrisisCenterModule } from './crisis-center/crisis-center.module'; -// #enddocregion crisis-center-module, admin-module -import { ComposeMessageComponent } from './compose-message.component'; -// #docregion admin-module +// #enddocregion crisis-center-module + import { AdminModule } from './admin/admin.module'; // #docregion crisis-center-module -import { DialogService } from './dialog.service'; - @NgModule({ imports: [ CommonModule, @@ -32,14 +30,11 @@ import { DialogService } from './dialog.service'; ], declarations: [ AppComponent, -// #enddocregion admin-module, crisis-center-module +// #enddocregion crisis-center-module ComposeMessageComponent, -// #docregion admin-module, crisis-center-module +// #docregion crisis-center-module PageNotFoundComponent ], - providers: [ - DialogService - ], bootstrap: [ AppComponent ] }) export class AppModule { } diff --git a/aio/content/examples/router/src/app/app.module.5.ts b/aio/content/examples/router/src/app/app.module.5.ts index ad34668cea..593c513e04 100644 --- a/aio/content/examples/router/src/app/app.module.5.ts +++ b/aio/content/examples/router/src/app/app.module.5.ts @@ -10,11 +10,10 @@ import { AppRoutingModule } from './app-routing.module'; import { HeroesModule } from './heroes/heroes.module'; import { CrisisCenterModule } from './crisis-center/crisis-center.module'; -import { ComposeMessageComponent } from './compose-message.component'; -import { PageNotFoundComponent } from './not-found.component'; +import { ComposeMessageComponent } from './compose-message/compose-message.component'; +import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; import { AdminModule } from './admin/admin.module'; -import { DialogService } from './dialog.service'; @NgModule({ imports: [ @@ -30,9 +29,6 @@ import { DialogService } from './dialog.service'; ComposeMessageComponent, PageNotFoundComponent ], - providers: [ - DialogService - ], bootstrap: [ AppComponent ] }) export class AppModule { } diff --git a/aio/content/examples/router/src/app/app.module.6.ts b/aio/content/examples/router/src/app/app.module.6.ts index 4cb0b1fdd5..6875f40d5e 100644 --- a/aio/content/examples/router/src/app/app.module.6.ts +++ b/aio/content/examples/router/src/app/app.module.6.ts @@ -5,7 +5,7 @@ import { FormsModule } from '@angular/forms'; import { Routes, RouterModule } from '@angular/router'; import { AppComponent } from './app.component'; -import { PageNotFoundComponent } from './not-found.component'; +import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; const routes: Routes = [ diff --git a/aio/content/examples/router/src/app/app.module.7.ts b/aio/content/examples/router/src/app/app.module.7.ts index b6ca81ddea..2e0428583d 100644 --- a/aio/content/examples/router/src/app/app.module.7.ts +++ b/aio/content/examples/router/src/app/app.module.7.ts @@ -2,18 +2,16 @@ import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { FormsModule } from '@angular/forms'; +import { Router } from '@angular/router'; -import { AppComponent } from './app.component'; -import { AppRoutingModule } from './app-routing.module'; +import { AppComponent } from './app.component'; +import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; +import { ComposeMessageComponent } from './compose-message/compose-message.component'; +import { AppRoutingModule } from './app-routing.module'; import { HeroesModule } from './heroes/heroes.module'; import { CrisisCenterModule } from './crisis-center/crisis-center.module'; -import { ComposeMessageComponent } from './compose-message.component'; -import { LoginRoutingModule } from './login-routing.module'; -import { LoginComponent } from './login.component'; -import { PageNotFoundComponent } from './not-found.component'; - -import { DialogService } from './dialog.service'; +import { AuthModule } from './auth/auth.module'; @NgModule({ imports: [ @@ -21,18 +19,24 @@ import { DialogService } from './dialog.service'; FormsModule, HeroesModule, CrisisCenterModule, - LoginRoutingModule, + AuthModule, AppRoutingModule ], declarations: [ AppComponent, ComposeMessageComponent, - LoginComponent, PageNotFoundComponent ], - providers: [ - DialogService - ], bootstrap: [ AppComponent ] }) -export class AppModule { } +// #docregion inspect-config +export class AppModule { + // Diagnostic only: inspect router configuration + constructor(router: Router) { + // Use a custom replacer to display function names in the route configs + const replacer = (key, value) => (typeof value === 'function') ? value.name : value; + + console.log('Routes: ', JSON.stringify(router.config, replacer, 2)); + } +} +// #enddocregion inspect-config diff --git a/aio/content/examples/router/src/app/app.module.ts b/aio/content/examples/router/src/app/app.module.ts index dcf3401ded..38168eb011 100644 --- a/aio/content/examples/router/src/app/app.module.ts +++ b/aio/content/examples/router/src/app/app.module.ts @@ -1,56 +1,58 @@ // #docplaster -// #docregion +// #docregion auth, preload import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { FormsModule } from '@angular/forms'; // #docregion animations-module import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -// #enddocregion animations-module +// #enddocregion auth, animations-module // #docregion inspect-config import { Router } from '@angular/router'; // #enddocregion inspect-config +// #docregion auth import { AppComponent } from './app.component'; +import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; +import { ComposeMessageComponent } from './compose-message/compose-message.component'; + import { AppRoutingModule } from './app-routing.module'; - import { HeroesModule } from './heroes/heroes.module'; -import { ComposeMessageComponent } from './compose-message.component'; -import { LoginRoutingModule } from './login-routing.module'; -import { LoginComponent } from './login.component'; -import { PageNotFoundComponent } from './not-found.component'; - -import { DialogService } from './dialog.service'; +import { AuthModule } from './auth/auth.module'; // #docregion animations-module @NgModule({ imports: [ // #enddocregion animations-module BrowserModule, + // #docregion animations-module + BrowserAnimationsModule, + // #enddocregion animations-module FormsModule, HeroesModule, - LoginRoutingModule, + AuthModule, AppRoutingModule, // #docregion animations-module - BrowserAnimationsModule - // #enddocregion animations-module ], + // #enddocregion animations-module declarations: [ AppComponent, ComposeMessageComponent, - LoginComponent, PageNotFoundComponent ], - providers: [ - DialogService - ], bootstrap: [ AppComponent ] +// #docregion animations-module }) -// #docregion inspect-config +// #enddocregion animations-module export class AppModule { +// #enddocregion preload, auth // Diagnostic only: inspect router configuration constructor(router: Router) { - console.log('Routes: ', JSON.stringify(router.config, undefined, 2)); + // Use a custom replacer to display function names in the route configs + // const replacer = (key, value) => (typeof value === 'function') ? value.name : value; + + // console.log('Routes: ', JSON.stringify(router.config, replacer, 2)); } +// #docregion preload, auth } -// #enddocregion inspect-config +// #enddocregion preload, auth diff --git a/aio/content/examples/router/src/app/auth-guard.service.1.ts b/aio/content/examples/router/src/app/auth-guard.service.1.ts deleted file mode 100644 index c824bcb208..0000000000 --- a/aio/content/examples/router/src/app/auth-guard.service.1.ts +++ /dev/null @@ -1,11 +0,0 @@ -// #docregion -import { Injectable } from '@angular/core'; -import { CanActivate } from '@angular/router'; - -@Injectable() -export class AuthGuard implements CanActivate { - canActivate() { - console.log('AuthGuard#canActivate called'); - return true; - } -} diff --git a/aio/content/examples/router/src/app/login-routing.module.ts b/aio/content/examples/router/src/app/auth/auth-routing.module.ts similarity index 52% rename from aio/content/examples/router/src/app/login-routing.module.ts rename to aio/content/examples/router/src/app/auth/auth-routing.module.ts index 96d05e7972..78d28ddfc8 100644 --- a/aio/content/examples/router/src/app/login-routing.module.ts +++ b/aio/content/examples/router/src/app/auth/auth-routing.module.ts @@ -1,24 +1,20 @@ // #docregion import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { AuthGuard } from './auth-guard.service'; +import { AuthGuard } from './auth.guard'; import { AuthService } from './auth.service'; -import { LoginComponent } from './login.component'; +import { LoginComponent } from './login/login.component'; -const loginRoutes: Routes = [ +const authRoutes: Routes = [ { path: 'login', component: LoginComponent } ]; @NgModule({ imports: [ - RouterModule.forChild(loginRoutes) + RouterModule.forChild(authRoutes) ], exports: [ RouterModule - ], - providers: [ - AuthGuard, - AuthService ] }) -export class LoginRoutingModule {} +export class AuthRoutingModule {} diff --git a/aio/content/examples/router/src/app/auth/auth.guard.1.ts b/aio/content/examples/router/src/app/auth/auth.guard.1.ts new file mode 100644 index 0000000000..a55d52100c --- /dev/null +++ b/aio/content/examples/router/src/app/auth/auth.guard.1.ts @@ -0,0 +1,16 @@ +// #docregion +import { Injectable } from '@angular/core'; +import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; +import { Observable } from 'rxjs/Observable'; + +@Injectable({ + providedIn: 'root' +}) +export class AuthGuard implements CanActivate { + canActivate( + next: ActivatedRouteSnapshot, + state: RouterStateSnapshot): boolean { + console.log('AuthGuard#canActivate called'); + return true; + } +} diff --git a/aio/content/examples/router/src/app/auth-guard.service.2.ts b/aio/content/examples/router/src/app/auth/auth.guard.2.ts similarity index 68% rename from aio/content/examples/router/src/app/auth-guard.service.2.ts rename to aio/content/examples/router/src/app/auth/auth.guard.2.ts index 8fd00e151a..08c289a718 100644 --- a/aio/content/examples/router/src/app/auth-guard.service.2.ts +++ b/aio/content/examples/router/src/app/auth/auth.guard.2.ts @@ -1,17 +1,19 @@ // #docregion -import { Injectable } from '@angular/core'; -import { - CanActivate, Router, - ActivatedRouteSnapshot, - RouterStateSnapshot -} from '@angular/router'; +import { Injectable } from '@angular/core'; +import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router'; +import { Observable } from 'rxjs/Observable'; + import { AuthService } from './auth.service'; -@Injectable() +@Injectable({ + providedIn: 'root' +}) export class AuthGuard implements CanActivate { constructor(private authService: AuthService, private router: Router) {} - canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { + canActivate( + next: ActivatedRouteSnapshot, + state: RouterStateSnapshot): boolean { let url: string = state.url; return this.checkLogin(url); diff --git a/aio/content/examples/router/src/app/auth-guard.service.3.ts b/aio/content/examples/router/src/app/auth/auth.guard.3.ts similarity index 79% rename from aio/content/examples/router/src/app/auth-guard.service.3.ts rename to aio/content/examples/router/src/app/auth/auth.guard.3.ts index dd89006411..5bb0c47a15 100644 --- a/aio/content/examples/router/src/app/auth-guard.service.3.ts +++ b/aio/content/examples/router/src/app/auth/auth.guard.3.ts @@ -1,4 +1,3 @@ -// #docregion // #docregion can-activate-child import { Injectable } from '@angular/core'; import { @@ -9,17 +8,23 @@ import { } from '@angular/router'; import { AuthService } from './auth.service'; -@Injectable() +@Injectable({ + providedIn: 'root' +}) export class AuthGuard implements CanActivate, CanActivateChild { constructor(private authService: AuthService, private router: Router) {} - canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { + canActivate( + route: ActivatedRouteSnapshot, + state: RouterStateSnapshot): boolean { let url: string = state.url; return this.checkLogin(url); } - canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { + canActivateChild( + route: ActivatedRouteSnapshot, + state: RouterStateSnapshot): boolean { return this.canActivate(route, state); } diff --git a/aio/content/examples/router/src/app/auth-guard.service.4.ts b/aio/content/examples/router/src/app/auth/auth.guard.4.ts similarity index 97% rename from aio/content/examples/router/src/app/auth-guard.service.4.ts rename to aio/content/examples/router/src/app/auth/auth.guard.4.ts index 5d239a8432..f460682c5f 100644 --- a/aio/content/examples/router/src/app/auth-guard.service.4.ts +++ b/aio/content/examples/router/src/app/auth/auth.guard.4.ts @@ -10,7 +10,9 @@ import { } from '@angular/router'; import { AuthService } from './auth.service'; -@Injectable() +@Injectable({ + providedIn: 'root' +}) export class AuthGuard implements CanActivate, CanActivateChild { constructor(private authService: AuthService, private router: Router) {} diff --git a/aio/content/examples/router/src/app/auth-guard.service.ts b/aio/content/examples/router/src/app/auth/auth.guard.ts similarity index 97% rename from aio/content/examples/router/src/app/auth-guard.service.ts rename to aio/content/examples/router/src/app/auth/auth.guard.ts index a32b5cc2b8..da9f79cb2b 100644 --- a/aio/content/examples/router/src/app/auth-guard.service.ts +++ b/aio/content/examples/router/src/app/auth/auth.guard.ts @@ -10,7 +10,9 @@ import { } from '@angular/router'; import { AuthService } from './auth.service'; -@Injectable() +@Injectable({ + providedIn: 'root' +}) export class AuthGuard implements CanActivate, CanActivateChild, CanLoad { constructor(private authService: AuthService, private router: Router) {} diff --git a/aio/content/examples/router/src/app/auth/auth.module.ts b/aio/content/examples/router/src/app/auth/auth.module.ts new file mode 100644 index 0000000000..f81d867862 --- /dev/null +++ b/aio/content/examples/router/src/app/auth/auth.module.ts @@ -0,0 +1,25 @@ +// #docplaster +// #docregion +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; + +import { LoginComponent } from './login/login.component'; +import { AuthRoutingModule } from './auth-routing.module'; + +// #docregion v1 +@NgModule({ + imports: [ + CommonModule, + FormsModule, +// #enddocregion v1 + AuthRoutingModule +// #docregion v1 + ], + declarations: [ + LoginComponent + ] +}) +export class AuthModule {} +// #enddocregion v1 +// #enddocregion diff --git a/aio/content/examples/router/src/app/auth.service.ts b/aio/content/examples/router/src/app/auth/auth.service.ts similarity index 92% rename from aio/content/examples/router/src/app/auth.service.ts rename to aio/content/examples/router/src/app/auth/auth.service.ts index 9978541065..6627d28fd6 100644 --- a/aio/content/examples/router/src/app/auth.service.ts +++ b/aio/content/examples/router/src/app/auth/auth.service.ts @@ -4,7 +4,9 @@ import { Injectable } from '@angular/core'; import { Observable, of } from 'rxjs'; import { tap, delay } from 'rxjs/operators'; -@Injectable() +@Injectable({ + providedIn: 'root' +}) export class AuthService { isLoggedIn = false; diff --git a/aio/content/examples/router/src/app/login.component.1.ts b/aio/content/examples/router/src/app/auth/login/login.component.1.ts similarity index 76% rename from aio/content/examples/router/src/app/login.component.1.ts rename to aio/content/examples/router/src/app/auth/login/login.component.1.ts index ddee339011..2986631aa1 100644 --- a/aio/content/examples/router/src/app/login.component.1.ts +++ b/aio/content/examples/router/src/app/auth/login/login.component.1.ts @@ -1,16 +1,12 @@ // #docregion import { Component } from '@angular/core'; import { Router } from '@angular/router'; -import { AuthService } from './auth.service'; +import { AuthService } from '../auth.service'; @Component({ - template: ` -

LOGIN

-

{{message}}

-

- - -

` + selector: 'app-login', + templateUrl: './login.component.html', + styleUrls: ['./login.component.css'] }) export class LoginComponent { message: string; diff --git a/aio/content/examples/router/src/app/auth/login/login.component.css b/aio/content/examples/router/src/app/auth/login/login.component.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/aio/content/examples/router/src/app/auth/login/login.component.html b/aio/content/examples/router/src/app/auth/login/login.component.html new file mode 100644 index 0000000000..adac1fb788 --- /dev/null +++ b/aio/content/examples/router/src/app/auth/login/login.component.html @@ -0,0 +1,6 @@ +

LOGIN

+

{{message}}

+

+ + +

\ No newline at end of file diff --git a/aio/content/examples/router/src/app/login.component.ts b/aio/content/examples/router/src/app/auth/login/login.component.ts similarity index 81% rename from aio/content/examples/router/src/app/login.component.ts rename to aio/content/examples/router/src/app/auth/login/login.component.ts index 1a6fae162f..5ebfd58b21 100644 --- a/aio/content/examples/router/src/app/login.component.ts +++ b/aio/content/examples/router/src/app/auth/login/login.component.ts @@ -2,16 +2,12 @@ import { Component } from '@angular/core'; import { Router, NavigationExtras } from '@angular/router'; -import { AuthService } from './auth.service'; +import { AuthService } from '../auth.service'; @Component({ - template: ` -

LOGIN

-

{{message}}

-

- - -

` + selector: 'app-login', + templateUrl: './login.component.html', + styleUrls: ['./login.component.css'] }) export class LoginComponent { message: string; diff --git a/aio/content/examples/router/src/app/can-deactivate-guard.service.1.ts b/aio/content/examples/router/src/app/can-deactivate.guard.1.ts similarity index 93% rename from aio/content/examples/router/src/app/can-deactivate-guard.service.1.ts rename to aio/content/examples/router/src/app/can-deactivate.guard.1.ts index 35af6226fb..45ccd1726d 100644 --- a/aio/content/examples/router/src/app/can-deactivate-guard.service.1.ts +++ b/aio/content/examples/router/src/app/can-deactivate.guard.1.ts @@ -5,9 +5,11 @@ import { CanDeactivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; -import { CrisisDetailComponent } from './crisis-center/crisis-detail.component'; +import { CrisisDetailComponent } from './crisis-center/crisis-detail/crisis-detail.component'; -@Injectable() +@Injectable({ + providedIn: 'root' +}) export class CanDeactivateGuard implements CanDeactivate { canDeactivate( diff --git a/aio/content/examples/router/src/app/can-deactivate-guard.service.ts b/aio/content/examples/router/src/app/can-deactivate.guard.ts similarity index 92% rename from aio/content/examples/router/src/app/can-deactivate-guard.service.ts rename to aio/content/examples/router/src/app/can-deactivate.guard.ts index e001d95ed9..1c947440a3 100644 --- a/aio/content/examples/router/src/app/can-deactivate-guard.service.ts +++ b/aio/content/examples/router/src/app/can-deactivate.guard.ts @@ -7,7 +7,9 @@ export interface CanComponentDeactivate { canDeactivate: () => Observable | Promise | boolean; } -@Injectable() +@Injectable({ + providedIn: 'root' +}) export class CanDeactivateGuard implements CanDeactivate { canDeactivate(component: CanComponentDeactivate) { return component.canDeactivate ? component.canDeactivate() : true; diff --git a/aio/content/examples/router/src/app/compose-message/compose-message.component.css b/aio/content/examples/router/src/app/compose-message/compose-message.component.css new file mode 100644 index 0000000000..c436ef4952 --- /dev/null +++ b/aio/content/examples/router/src/app/compose-message/compose-message.component.css @@ -0,0 +1,3 @@ +:host { + position: relative; bottom: 10%; +} \ No newline at end of file diff --git a/aio/content/examples/router/src/app/compose-message.component.html b/aio/content/examples/router/src/app/compose-message/compose-message.component.html similarity index 100% rename from aio/content/examples/router/src/app/compose-message.component.html rename to aio/content/examples/router/src/app/compose-message/compose-message.component.html diff --git a/aio/content/examples/router/src/app/compose-message.component.ts b/aio/content/examples/router/src/app/compose-message/compose-message.component.ts similarity index 90% rename from aio/content/examples/router/src/app/compose-message.component.ts rename to aio/content/examples/router/src/app/compose-message/compose-message.component.ts index e243d21c75..445eb8e396 100644 --- a/aio/content/examples/router/src/app/compose-message.component.ts +++ b/aio/content/examples/router/src/app/compose-message/compose-message.component.ts @@ -3,8 +3,9 @@ import { Component, HostBinding } from '@angular/core'; import { Router } from '@angular/router'; @Component({ + selector: 'app-compose-message', templateUrl: './compose-message.component.html', - styles: [ ':host { position: relative; bottom: 10%; }' ] + styleUrls: ['./compose-message.component.css'] }) export class ComposeMessageComponent { details: string; diff --git a/aio/content/examples/router/src/app/crisis-center/crisis-center-home.component.ts b/aio/content/examples/router/src/app/crisis-center/crisis-center-home.component.ts deleted file mode 100644 index 0edc35bc6e..0000000000 --- a/aio/content/examples/router/src/app/crisis-center/crisis-center-home.component.ts +++ /dev/null @@ -1,9 +0,0 @@ -// #docregion -import { Component } from '@angular/core'; - -@Component({ - template: ` -

Welcome to the Crisis Center

- ` -}) -export class CrisisCenterHomeComponent { } diff --git a/aio/content/examples/router/src/app/crisis-center/crisis-center-home/crisis-center-home.component.css b/aio/content/examples/router/src/app/crisis-center/crisis-center-home/crisis-center-home.component.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/aio/content/examples/router/src/app/crisis-center/crisis-center-home/crisis-center-home.component.html b/aio/content/examples/router/src/app/crisis-center/crisis-center-home/crisis-center-home.component.html new file mode 100644 index 0000000000..f3c7d4e50c --- /dev/null +++ b/aio/content/examples/router/src/app/crisis-center/crisis-center-home/crisis-center-home.component.html @@ -0,0 +1 @@ +

Welcome to the Crisis Center

\ No newline at end of file diff --git a/aio/content/examples/router/src/app/crisis-center/crisis-center-home/crisis-center-home.component.ts b/aio/content/examples/router/src/app/crisis-center/crisis-center-home/crisis-center-home.component.ts new file mode 100644 index 0000000000..c2b7cde814 --- /dev/null +++ b/aio/content/examples/router/src/app/crisis-center/crisis-center-home/crisis-center-home.component.ts @@ -0,0 +1,9 @@ +// #docregion +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-crisis-center-home', + templateUrl: './crisis-center-home.component.html', + styleUrls: ['./crisis-center-home.component.css'] +}) +export class CrisisCenterHomeComponent { } diff --git a/aio/content/examples/router/src/app/crisis-center/crisis-center-routing.module.1.ts b/aio/content/examples/router/src/app/crisis-center/crisis-center-routing.module.1.ts index 8ef60e68a1..c002b2ebb3 100644 --- a/aio/content/examples/router/src/app/crisis-center/crisis-center-routing.module.1.ts +++ b/aio/content/examples/router/src/app/crisis-center/crisis-center-routing.module.1.ts @@ -3,10 +3,10 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { CrisisCenterHomeComponent } from './crisis-center-home.component'; -import { CrisisListComponent } from './crisis-list.component'; -import { CrisisCenterComponent } from './crisis-center.component'; -import { CrisisDetailComponent } from './crisis-detail.component'; +import { CrisisCenterHomeComponent } from './crisis-center-home/crisis-center-home.component'; +import { CrisisListComponent } from './crisis-list/crisis-list.component'; +import { CrisisCenterComponent } from './crisis-center/crisis-center.component'; +import { CrisisDetailComponent } from './crisis-detail/crisis-detail.component'; // #docregion routes const crisisCenterRoutes: Routes = [ diff --git a/aio/content/examples/router/src/app/crisis-center/crisis-center-routing.module.2.ts b/aio/content/examples/router/src/app/crisis-center/crisis-center-routing.module.2.ts index 9e9b514968..5bc0dfee02 100644 --- a/aio/content/examples/router/src/app/crisis-center/crisis-center-routing.module.2.ts +++ b/aio/content/examples/router/src/app/crisis-center/crisis-center-routing.module.2.ts @@ -3,30 +3,23 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { CrisisCenterHomeComponent } from './crisis-center-home.component'; -import { CrisisListComponent } from './crisis-list.component'; -import { CrisisCenterComponent } from './crisis-center.component'; -import { CrisisDetailComponent } from './crisis-detail.component'; +import { CrisisCenterHomeComponent } from './crisis-center-home/crisis-center-home.component'; +import { CrisisListComponent } from './crisis-list/crisis-list.component'; +import { CrisisCenterComponent } from './crisis-center/crisis-center.component'; +import { CrisisDetailComponent } from './crisis-detail/crisis-detail.component'; // #enddocregion routes // #docregion can-deactivate-guard -import { CanDeactivateGuard } from '../can-deactivate-guard.service'; +import { CanDeactivateGuard } from '../can-deactivate.guard'; // #enddocregion can-deactivate-guard // #docregion crisis-detail-resolver -import { CrisisDetailResolver } from './crisis-detail-resolver.service'; +import { CrisisDetailResolverService } from './crisis-detail-resolver.service'; // #enddocregion crisis-detail-resolver // #docregion routes const crisisCenterRoutes: Routes = [ // #enddocregion routes - // #docregion redirect, routes - { - path: '', - redirectTo: '/crisis-center', - pathMatch: 'full' - }, - // #enddocregion redirect, routes // #docregion routes { path: 'crisis-center', @@ -45,7 +38,7 @@ const crisisCenterRoutes: Routes = [ // #enddocregion can-deactivate-guard // #docregion crisis-detail-resolver resolve: { - crisis: CrisisDetailResolver + crisis: CrisisDetailResolverService } // #enddocregion crisis-detail-resolver // #docregion routes diff --git a/aio/content/examples/router/src/app/crisis-center/crisis-center-routing.module.3.ts b/aio/content/examples/router/src/app/crisis-center/crisis-center-routing.module.3.ts index 6d605dbe84..f134bd34e9 100644 --- a/aio/content/examples/router/src/app/crisis-center/crisis-center-routing.module.3.ts +++ b/aio/content/examples/router/src/app/crisis-center/crisis-center-routing.module.3.ts @@ -3,20 +3,15 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { CrisisCenterHomeComponent } from './crisis-center-home.component'; -import { CrisisListComponent } from './crisis-list.component'; -import { CrisisCenterComponent } from './crisis-center.component'; -import { CrisisDetailComponent } from './crisis-detail.component'; +import { CrisisCenterHomeComponent } from './crisis-center-home/crisis-center-home.component'; +import { CrisisListComponent } from './crisis-list/crisis-list.component'; +import { CrisisCenterComponent } from './crisis-center/crisis-center.component'; +import { CrisisDetailComponent } from './crisis-detail/crisis-detail.component'; // #docregion can-deactivate-guard -import { CanDeactivateGuard } from '../can-deactivate-guard.service'; +import { CanDeactivateGuard } from '../can-deactivate.guard'; const crisisCenterRoutes: Routes = [ - { - path: '', - redirectTo: '/crisis-center', - pathMatch: 'full' - }, { path: 'crisis-center', component: CrisisCenterComponent, diff --git a/aio/content/examples/router/src/app/crisis-center/crisis-center-routing.module.4.ts b/aio/content/examples/router/src/app/crisis-center/crisis-center-routing.module.4.ts index b7ac88e852..03ffed2ec2 100644 --- a/aio/content/examples/router/src/app/crisis-center/crisis-center-routing.module.4.ts +++ b/aio/content/examples/router/src/app/crisis-center/crisis-center-routing.module.4.ts @@ -3,25 +3,15 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { CrisisCenterHomeComponent } from './crisis-center-home.component'; -import { CrisisListComponent } from './crisis-list.component'; -import { CrisisCenterComponent } from './crisis-center.component'; -import { CrisisDetailComponent } from './crisis-detail.component'; +import { CrisisCenterHomeComponent } from './crisis-center-home/crisis-center-home.component'; +import { CrisisListComponent } from './crisis-list/crisis-list.component'; +import { CrisisCenterComponent } from './crisis-center/crisis-center.component'; +import { CrisisDetailComponent } from './crisis-detail/crisis-detail.component'; -import { CanDeactivateGuard } from '../can-deactivate-guard.service'; +import { CanDeactivateGuard } from '../can-deactivate.guard'; +import { CrisisDetailResolverService } from './crisis-detail-resolver.service'; -// #docregion crisis-detail-resolver -import { CrisisDetailResolver } from './crisis-detail-resolver.service'; - -// #enddocregion crisis-detail-resolver const crisisCenterRoutes: Routes = [ - // #docregion redirect - { - path: '', - redirectTo: '/crisis-center', - pathMatch: 'full' - }, - // #enddocregion redirect { path: 'crisis-center', component: CrisisCenterComponent, @@ -35,7 +25,7 @@ const crisisCenterRoutes: Routes = [ component: CrisisDetailComponent, canDeactivate: [CanDeactivateGuard], resolve: { - crisis: CrisisDetailResolver + crisis: CrisisDetailResolverService } }, { @@ -48,18 +38,13 @@ const crisisCenterRoutes: Routes = [ } ]; -// #docregion crisis-detail-resolver @NgModule({ imports: [ RouterModule.forChild(crisisCenterRoutes) ], exports: [ RouterModule - ], - providers: [ - CrisisDetailResolver ] }) export class CrisisCenterRoutingModule { } -// #enddocregion crisis-detail-resolver // #enddocregion diff --git a/aio/content/examples/router/src/app/crisis-center/crisis-center-routing.module.ts b/aio/content/examples/router/src/app/crisis-center/crisis-center-routing.module.ts index c01d592455..393deb7d8d 100644 --- a/aio/content/examples/router/src/app/crisis-center/crisis-center-routing.module.ts +++ b/aio/content/examples/router/src/app/crisis-center/crisis-center-routing.module.ts @@ -3,13 +3,13 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { CrisisCenterHomeComponent } from './crisis-center-home.component'; -import { CrisisListComponent } from './crisis-list.component'; -import { CrisisCenterComponent } from './crisis-center.component'; -import { CrisisDetailComponent } from './crisis-detail.component'; +import { CrisisCenterHomeComponent } from './crisis-center-home/crisis-center-home.component'; +import { CrisisListComponent } from './crisis-list/crisis-list.component'; +import { CrisisCenterComponent } from './crisis-center/crisis-center.component'; +import { CrisisDetailComponent } from './crisis-detail/crisis-detail.component'; -import { CanDeactivateGuard } from '../can-deactivate-guard.service'; -import { CrisisDetailResolver } from './crisis-detail-resolver.service'; +import { CanDeactivateGuard } from '../can-deactivate.guard'; +import { CrisisDetailResolverService } from './crisis-detail-resolver.service'; const crisisCenterRoutes: Routes = [ { @@ -25,7 +25,7 @@ const crisisCenterRoutes: Routes = [ component: CrisisDetailComponent, canDeactivate: [CanDeactivateGuard], resolve: { - crisis: CrisisDetailResolver + crisis: CrisisDetailResolverService } }, { @@ -44,9 +44,6 @@ const crisisCenterRoutes: Routes = [ ], exports: [ RouterModule - ], - providers: [ - CrisisDetailResolver ] }) export class CrisisCenterRoutingModule { } diff --git a/aio/content/examples/router/src/app/crisis-center/crisis-center.component.ts b/aio/content/examples/router/src/app/crisis-center/crisis-center.component.ts deleted file mode 100644 index c7d7fe412d..0000000000 --- a/aio/content/examples/router/src/app/crisis-center/crisis-center.component.ts +++ /dev/null @@ -1,10 +0,0 @@ -// #docregion -import { Component } from '@angular/core'; - -@Component({ - template: ` -

CRISIS CENTER

- - ` -}) -export class CrisisCenterComponent { } diff --git a/aio/content/examples/router/src/app/crisis-center/crisis-center.module.1.ts b/aio/content/examples/router/src/app/crisis-center/crisis-center.module.1.ts deleted file mode 100644 index 5a3e45f58f..0000000000 --- a/aio/content/examples/router/src/app/crisis-center/crisis-center.module.1.ts +++ /dev/null @@ -1,36 +0,0 @@ -// #docplaster -// #docregion -import { NgModule } from '@angular/core'; -import { FormsModule } from '@angular/forms'; -import { CommonModule } from '@angular/common'; - -import { CrisisService } from './crisis.service'; - -import { CrisisCenterComponent } from './crisis-center.component'; -import { CrisisListComponent } from './crisis-list.component'; -import { CrisisCenterHomeComponent } from './crisis-center-home.component'; -import { CrisisDetailComponent } from './crisis-detail.component'; - -import { CrisisCenterRoutingModule } from './crisis-center-routing.module'; - -@NgModule({ - imports: [ - CommonModule, - FormsModule, - CrisisCenterRoutingModule - ], - declarations: [ - CrisisCenterComponent, - CrisisListComponent, - CrisisCenterHomeComponent, - CrisisDetailComponent - ], - - // #docregion providers - providers: [ - CrisisService - ] - // #enddocregion providers -}) -export class CrisisCenterModule {} -// #enddocregion diff --git a/aio/content/examples/router/src/app/crisis-center/crisis-center.module.ts b/aio/content/examples/router/src/app/crisis-center/crisis-center.module.ts index 4061ceac60..fb6753011e 100644 --- a/aio/content/examples/router/src/app/crisis-center/crisis-center.module.ts +++ b/aio/content/examples/router/src/app/crisis-center/crisis-center.module.ts @@ -1,15 +1,12 @@ -// #docplaster // #docregion import { NgModule } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { CommonModule } from '@angular/common'; -import { CrisisService } from './crisis.service'; - -import { CrisisCenterComponent } from './crisis-center.component'; -import { CrisisListComponent } from './crisis-list.component'; -import { CrisisCenterHomeComponent } from './crisis-center-home.component'; -import { CrisisDetailComponent } from './crisis-detail.component'; +import { CrisisCenterHomeComponent } from './crisis-center-home/crisis-center-home.component'; +import { CrisisListComponent } from './crisis-list/crisis-list.component'; +import { CrisisCenterComponent } from './crisis-center/crisis-center.component'; +import { CrisisDetailComponent } from './crisis-detail/crisis-detail.component'; import { CrisisCenterRoutingModule } from './crisis-center-routing.module'; @@ -24,12 +21,6 @@ import { CrisisCenterRoutingModule } from './crisis-center-routing.module'; CrisisListComponent, CrisisCenterHomeComponent, CrisisDetailComponent - ], - providers: [ - CrisisService ] }) -// #docregion crisis-center-module-export export class CrisisCenterModule {} -// #enddocregion crisis-center-module-export -// #enddocregion diff --git a/aio/content/examples/router/src/app/crisis-center/crisis-center/crisis-center.component.css b/aio/content/examples/router/src/app/crisis-center/crisis-center/crisis-center.component.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/aio/content/examples/router/src/app/crisis-center/crisis-center/crisis-center.component.html b/aio/content/examples/router/src/app/crisis-center/crisis-center/crisis-center.component.html new file mode 100644 index 0000000000..f208bcd790 --- /dev/null +++ b/aio/content/examples/router/src/app/crisis-center/crisis-center/crisis-center.component.html @@ -0,0 +1,2 @@ +

CRISIS CENTER

+ \ No newline at end of file diff --git a/aio/content/examples/router/src/app/crisis-center/crisis-center/crisis-center.component.ts b/aio/content/examples/router/src/app/crisis-center/crisis-center/crisis-center.component.ts new file mode 100644 index 0000000000..3e0ac92cb8 --- /dev/null +++ b/aio/content/examples/router/src/app/crisis-center/crisis-center/crisis-center.component.ts @@ -0,0 +1,9 @@ +// #docregion +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-crisis-center', + templateUrl: './crisis-center.component.html', + styleUrls: ['./crisis-center.component.css'] +}) +export class CrisisCenterComponent { } diff --git a/aio/content/examples/router/src/app/crisis-center/crisis-detail-resolver.service.1.ts b/aio/content/examples/router/src/app/crisis-center/crisis-detail-resolver.service.1.ts new file mode 100644 index 0000000000..ded64f12cd --- /dev/null +++ b/aio/content/examples/router/src/app/crisis-center/crisis-detail-resolver.service.1.ts @@ -0,0 +1,11 @@ +// #docregion +import { Injectable } from '@angular/core'; + +@Injectable({ + providedIn: 'root' +}) +export class CrisisDetailResolverService { + + constructor() { } + +} diff --git a/aio/content/examples/router/src/app/crisis-center/crisis-detail-resolver.service.ts b/aio/content/examples/router/src/app/crisis-center/crisis-detail-resolver.service.ts index a861c3bb97..b801f9ead9 100644 --- a/aio/content/examples/router/src/app/crisis-center/crisis-detail-resolver.service.ts +++ b/aio/content/examples/router/src/app/crisis-center/crisis-detail-resolver.service.ts @@ -1,27 +1,31 @@ + // #docregion import { Injectable } from '@angular/core'; import { Router, Resolve, RouterStateSnapshot, ActivatedRouteSnapshot } from '@angular/router'; -import { Observable } from 'rxjs'; -import { map, take } from 'rxjs/operators'; +import { Observable, of, EMPTY as empty } from 'rxjs'; +import { mergeMap, take } from 'rxjs/operators'; -import { Crisis, CrisisService } from './crisis.service'; +import { CrisisService } from './crisis.service'; +import { Crisis } from './crisis'; -@Injectable() -export class CrisisDetailResolver implements Resolve { +@Injectable({ + providedIn: 'root' +}) +export class CrisisDetailResolverService implements Resolve { constructor(private cs: CrisisService, private router: Router) {} - resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { + resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable | Observable { let id = route.paramMap.get('id'); return this.cs.getCrisis(id).pipe( take(1), - map(crisis => { + mergeMap(crisis => { if (crisis) { - return crisis; + return of(crisis); } else { // id not found this.router.navigate(['/crisis-center']); - return null; + return empty; } }) ); diff --git a/aio/content/examples/router/src/app/crisis-center/crisis-detail.component.1.ts b/aio/content/examples/router/src/app/crisis-center/crisis-detail/crisis-detail.component.1.ts similarity index 76% rename from aio/content/examples/router/src/app/crisis-center/crisis-detail.component.1.ts rename to aio/content/examples/router/src/app/crisis-center/crisis-detail/crisis-detail.component.1.ts index 4ed913a18c..8a70cd9b86 100644 --- a/aio/content/examples/router/src/app/crisis-center/crisis-detail.component.1.ts +++ b/aio/content/examples/router/src/app/crisis-center/crisis-detail/crisis-detail.component.1.ts @@ -1,30 +1,18 @@ // #docplaster // #docregion -import { Component, OnInit, HostBinding } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router, ParamMap } from '@angular/router'; import { Observable } from 'rxjs'; import { switchMap } from 'rxjs/operators'; -import { Crisis, CrisisService } from './crisis.service'; -import { DialogService } from '../dialog.service'; +import { CrisisService } from '../crisis.service'; +import { Crisis } from '../crisis'; +import { DialogService } from '../../dialog.service'; @Component({ - template: ` -
-

"{{ editName }}"

-
- {{ crisis.id }}
-
- - -
-

- - -

-
- `, - styles: ['input {width: 20em}'] + selector: 'app-crisis-detail', + templateUrl: './crisis-detail.component.html', + styleUrls: ['./crisis-detail.component.css'] }) export class CrisisDetailComponent implements OnInit { crisis: Crisis; diff --git a/aio/content/examples/router/src/app/crisis-center/crisis-detail/crisis-detail.component.css b/aio/content/examples/router/src/app/crisis-center/crisis-detail/crisis-detail.component.css new file mode 100644 index 0000000000..1300202b35 --- /dev/null +++ b/aio/content/examples/router/src/app/crisis-center/crisis-detail/crisis-detail.component.css @@ -0,0 +1,3 @@ +input { + width: 20em +} \ No newline at end of file diff --git a/aio/content/examples/router/src/app/crisis-center/crisis-detail/crisis-detail.component.html b/aio/content/examples/router/src/app/crisis-center/crisis-detail/crisis-detail.component.html new file mode 100644 index 0000000000..524f839df1 --- /dev/null +++ b/aio/content/examples/router/src/app/crisis-center/crisis-detail/crisis-detail.component.html @@ -0,0 +1,13 @@ +
+

"{{ editName }}"

+
+ {{ crisis.id }}
+
+ + +
+

+ + +

+
\ No newline at end of file diff --git a/aio/content/examples/router/src/app/crisis-center/crisis-detail.component.ts b/aio/content/examples/router/src/app/crisis-center/crisis-detail/crisis-detail.component.ts similarity index 78% rename from aio/content/examples/router/src/app/crisis-center/crisis-detail.component.ts rename to aio/content/examples/router/src/app/crisis-center/crisis-detail/crisis-detail.component.ts index c3510a8df9..1aeeff58aa 100644 --- a/aio/content/examples/router/src/app/crisis-center/crisis-detail.component.ts +++ b/aio/content/examples/router/src/app/crisis-center/crisis-detail/crisis-detail.component.ts @@ -4,26 +4,13 @@ import { Component, OnInit, HostBinding } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { Observable } from 'rxjs'; -import { Crisis } from './crisis.service'; -import { DialogService } from '../dialog.service'; +import { Crisis } from '../crisis'; +import { DialogService } from '../../dialog.service'; @Component({ - template: ` -
-

"{{ editName }}"

-
- {{ crisis.id }}
-
- - -
-

- - -

-
- `, - styles: ['input {width: 20em}'] + selector: 'app-crisis-detail', + templateUrl: './crisis-detail.component.html', + styleUrls: ['./crisis-detail.component.css'] }) export class CrisisDetailComponent implements OnInit { crisis: Crisis; diff --git a/aio/content/examples/router/src/app/crisis-center/crisis-list.component.1.ts b/aio/content/examples/router/src/app/crisis-center/crisis-list.component.1.ts deleted file mode 100644 index 5bd95158bb..0000000000 --- a/aio/content/examples/router/src/app/crisis-center/crisis-list.component.1.ts +++ /dev/null @@ -1,44 +0,0 @@ - -import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute, ParamMap } from '@angular/router'; - -import { Crisis, CrisisService } from './crisis.service'; -import { Observable } from 'rxjs'; -import { switchMap } from 'rxjs/operators'; - -@Component({ - // #docregion relative-navigation-router-link - template: ` - - - - ` - // #enddocregion relative-navigation-router-link -}) -export class CrisisListComponent implements OnInit { - crises$: Observable; - selectedId: number; - - - constructor( - private service: CrisisService, - private route: ActivatedRoute - ) {} - - - ngOnInit() { - this.crises$ = this.route.paramMap.pipe( - switchMap((params: ParamMap) => { - this.selectedId = +params.get('id'); - return this.service.getCrises(); - }) - ); - } -} diff --git a/aio/content/examples/router/src/app/crisis-center/crisis-list.component.ts b/aio/content/examples/router/src/app/crisis-center/crisis-list/crisis-list.component.1.ts similarity index 59% rename from aio/content/examples/router/src/app/crisis-center/crisis-list.component.ts rename to aio/content/examples/router/src/app/crisis-center/crisis-list/crisis-list.component.1.ts index 3219f10212..eac3ba228e 100644 --- a/aio/content/examples/router/src/app/crisis-center/crisis-list.component.ts +++ b/aio/content/examples/router/src/app/crisis-center/crisis-list/crisis-list.component.1.ts @@ -1,35 +1,26 @@ -// #docregion + import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, ParamMap } from '@angular/router'; -import { Crisis, CrisisService } from './crisis.service'; +import { CrisisService } from '../crisis.service'; +import { Crisis } from '../crisis'; import { Observable } from 'rxjs'; import { switchMap } from 'rxjs/operators'; @Component({ - template: ` - - - - ` + selector: 'app-crisis-list', + templateUrl: './crisis-list.component.html', + styleUrls: ['./crisis-list.component.css'] }) export class CrisisListComponent implements OnInit { crises$: Observable; selectedId: number; - // #docregion ctor constructor( private service: CrisisService, private route: ActivatedRoute ) {} - // #enddocregion ctor + ngOnInit() { this.crises$ = this.route.paramMap.pipe( diff --git a/aio/content/examples/router/src/assets/app.css b/aio/content/examples/router/src/app/crisis-center/crisis-list/crisis-list.component.css similarity index 54% rename from aio/content/examples/router/src/assets/app.css rename to aio/content/examples/router/src/app/crisis-center/crisis-list/crisis-list.component.css index 8da7fa6567..65ce4b1e70 100644 --- a/aio/content/examples/router/src/assets/app.css +++ b/aio/content/examples/router/src/app/crisis-center/crisis-list/crisis-list.component.css @@ -1,41 +1,37 @@ -/* items class */ -.items { +/* CrisisListComponent's private CSS styles */ +.crises { margin: 0 0 2em 0; list-style-type: none; padding: 0; width: 24em; } -.items li { - cursor: pointer; +.crises li { position: relative; - left: 0; + cursor: pointer; background-color: #EEE; margin: .5em; padding: .3em 0; height: 1.6em; border-radius: 4px; } -.items li a { - display: block; - text-decoration: none; -} -.items li:hover { + +.crises li:hover { color: #607D8B; background-color: #DDD; left: .1em; } -.items li.selected { - background-color: #CFD8DC; - color: white; + +.crises a { + color: #888; + text-decoration: none; + display: block; } -.items li.selected:hover { - background-color: #BBD8DC; + +.crises a:hover { + color:#607D8B; } -.items .text { - position: relative; - top: -3px; -} -.items .badge { + +.crises .badge { display: inline-block; font-size: small; color: white; @@ -46,6 +42,38 @@ left: -1px; top: -4px; height: 1.8em; + min-width: 16px; + text-align: right; margin-right: .8em; border-radius: 4px 0 0 4px; } + +button { + background-color: #eee; + border: none; + padding: 5px 10px; + border-radius: 4px; + cursor: pointer; + cursor: hand; + font-family: Arial; +} + +button:hover { + background-color: #cfd8dc; +} + +button.delete { + position: relative; + left: 194px; + top: -32px; + background-color: gray !important; + color: white; +} + +.crises li.selected { + background-color: #CFD8DC; + color: white; +} +.crises li.selected:hover { + background-color: #BBD8DC; +} \ No newline at end of file diff --git a/aio/content/examples/router/src/app/crisis-center/crisis-list/crisis-list.component.html b/aio/content/examples/router/src/app/crisis-center/crisis-list/crisis-list.component.html new file mode 100644 index 0000000000..062bd6b319 --- /dev/null +++ b/aio/content/examples/router/src/app/crisis-center/crisis-list/crisis-list.component.html @@ -0,0 +1,12 @@ + + + + + \ No newline at end of file diff --git a/aio/content/examples/router/src/app/crisis-center/crisis-list/crisis-list.component.ts b/aio/content/examples/router/src/app/crisis-center/crisis-list/crisis-list.component.ts new file mode 100644 index 0000000000..21a6e63fb0 --- /dev/null +++ b/aio/content/examples/router/src/app/crisis-center/crisis-list/crisis-list.component.ts @@ -0,0 +1,34 @@ +// #docregion +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; + +import { CrisisService } from '../crisis.service'; +import { Crisis } from '../crisis'; +import { Observable } from 'rxjs'; +import { switchMap } from 'rxjs/operators'; + +@Component({ + selector: 'app-crisis-list', + templateUrl: './crisis-list.component.html', + styleUrls: ['./crisis-list.component.css'] +}) +export class CrisisListComponent implements OnInit { + crises$: Observable; + selectedId: number; + + // #docregion ctor + constructor( + private service: CrisisService, + private route: ActivatedRoute + ) {} + // #enddocregion ctor + + ngOnInit() { + this.crises$ = this.route.paramMap.pipe( + switchMap(params => { + this.selectedId = +params.get('id'); + return this.service.getCrises(); + }) + ); + } +} diff --git a/aio/content/examples/router/src/app/crisis-center/crisis.service.ts b/aio/content/examples/router/src/app/crisis-center/crisis.service.ts index 72b51316c7..417ed49d34 100644 --- a/aio/content/examples/router/src/app/crisis-center/crisis.service.ts +++ b/aio/content/examples/router/src/app/crisis-center/crisis.service.ts @@ -1,27 +1,22 @@ // #docplaster -// #docregion , mock-crises +// #docregion import { BehaviorSubject } from 'rxjs'; import { map } from 'rxjs/operators'; -export class Crisis { - constructor(public id: number, public name: string) { } -} - -const CRISES = [ - new Crisis(1, 'Dragon Burning Cities'), - new Crisis(2, 'Sky Rains Great White Sharks'), - new Crisis(3, 'Giant Asteroid Heading For Earth'), - new Crisis(4, 'Procrastinators Meeting Delayed Again'), -]; -// #enddocregion mock-crises - import { Injectable } from '@angular/core'; +import { MessageService } from '../message.service'; +import { Crisis } from './crisis'; +import { CRISES } from './mock-crises'; -@Injectable() +@Injectable({ + providedIn: 'root' +}) export class CrisisService { static nextCrisisId = 100; private crises$: BehaviorSubject = new BehaviorSubject(CRISES); + constructor(private messageService: MessageService) { } + getCrises() { return this.crises$; } getCrisis(id: number | string) { @@ -34,7 +29,7 @@ export class CrisisService { addCrisis(name: string) { name = name.trim(); if (name) { - let crisis = new Crisis(CrisisService.nextCrisisId++, name); + let crisis = { id: CrisisService.nextCrisisId++, name }; CRISES.push(crisis); this.crises$.next(CRISES); } diff --git a/aio/content/examples/router/src/app/crisis-center/crisis.ts b/aio/content/examples/router/src/app/crisis-center/crisis.ts new file mode 100644 index 0000000000..3b89ceec79 --- /dev/null +++ b/aio/content/examples/router/src/app/crisis-center/crisis.ts @@ -0,0 +1,4 @@ +export class Crisis { + id: number; + name: string; +} diff --git a/aio/content/examples/router/src/app/crisis-center/mock-crises.ts b/aio/content/examples/router/src/app/crisis-center/mock-crises.ts new file mode 100644 index 0000000000..1870a86170 --- /dev/null +++ b/aio/content/examples/router/src/app/crisis-center/mock-crises.ts @@ -0,0 +1,9 @@ +// #docregion +import { Crisis } from './crisis'; + +export const CRISES: Crisis[] = [ + { id: 1, name: 'Dragon Burning Cities' }, + { id: 2, name: 'Sky Rains Great White Sharks' }, + { id: 3, name: 'Giant Asteroid Heading For Earth' }, + { id: 4, name: 'Procrastinators Meeting Delayed Again' }, +] diff --git a/aio/content/examples/router/src/app/crisis-list/crisis-list.component.css b/aio/content/examples/router/src/app/crisis-list/crisis-list.component.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/aio/content/examples/router/src/app/crisis-list/crisis-list.component.html b/aio/content/examples/router/src/app/crisis-list/crisis-list.component.html new file mode 100644 index 0000000000..aabd2a641e --- /dev/null +++ b/aio/content/examples/router/src/app/crisis-list/crisis-list.component.html @@ -0,0 +1,2 @@ +

CRISIS CENTER

+

Get your crisis here

diff --git a/aio/content/examples/router/src/app/crisis-list.component.ts b/aio/content/examples/router/src/app/crisis-list/crisis-list.component.ts similarity index 52% rename from aio/content/examples/router/src/app/crisis-list.component.ts rename to aio/content/examples/router/src/app/crisis-list/crisis-list.component.ts index 6caa3653b5..2fa2e05e49 100644 --- a/aio/content/examples/router/src/app/crisis-list.component.ts +++ b/aio/content/examples/router/src/app/crisis-list/crisis-list.component.ts @@ -3,8 +3,8 @@ import { Component } from '@angular/core'; @Component({ - template: ` -

CRISIS CENTER

-

Get your crisis here

` + selector: 'app-crisis-list', + templateUrl: './crisis-list.component.html', + styleUrls: ['./crisis-list.component.css'] }) export class CrisisListComponent { } diff --git a/aio/content/examples/router/src/app/dialog.service.ts b/aio/content/examples/router/src/app/dialog.service.ts index d9f7f1e163..218822bc0e 100644 --- a/aio/content/examples/router/src/app/dialog.service.ts +++ b/aio/content/examples/router/src/app/dialog.service.ts @@ -7,7 +7,9 @@ import { Observable, of } from 'rxjs'; * DialogService makes this app easier to test by faking this service. * TODO: better modal implementation that doesn't use window.confirm */ -@Injectable() +@Injectable({ + providedIn: 'root' +}) export class DialogService { /** * Ask user to confirm an action. `message` explains the action and choices. diff --git a/aio/content/examples/router/src/app/hero-list.component.ts b/aio/content/examples/router/src/app/hero-list.component.ts deleted file mode 100644 index 7a8f97ca1e..0000000000 --- a/aio/content/examples/router/src/app/hero-list.component.ts +++ /dev/null @@ -1,13 +0,0 @@ -/// Initial empty version -// #docregion -import { Component } from '@angular/core'; - -@Component({ - template: ` -

HEROES

-

Get your heroes here

- - - ` -}) -export class HeroListComponent { } diff --git a/aio/content/examples/router/src/app/hero-list/hero-list.component.css b/aio/content/examples/router/src/app/hero-list/hero-list.component.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/aio/content/examples/router/src/app/hero-list/hero-list.component.html b/aio/content/examples/router/src/app/hero-list/hero-list.component.html new file mode 100644 index 0000000000..58747e208a --- /dev/null +++ b/aio/content/examples/router/src/app/hero-list/hero-list.component.html @@ -0,0 +1,6 @@ + +

HEROES

+

Get your heroes here

+ + + diff --git a/aio/content/examples/router/src/app/hero-list/hero-list.component.ts b/aio/content/examples/router/src/app/hero-list/hero-list.component.ts new file mode 100644 index 0000000000..5ee06903c0 --- /dev/null +++ b/aio/content/examples/router/src/app/hero-list/hero-list.component.ts @@ -0,0 +1,10 @@ + +// #docregion +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-hero-list', + templateUrl: './hero-list.component.html', + styleUrls: ['./hero-list.component.css'] +}) +export class HeroListComponent { } diff --git a/aio/content/examples/router/src/app/heroes/hero-detail.component.1.ts b/aio/content/examples/router/src/app/heroes/hero-detail/hero-detail.component.1.ts similarity index 70% rename from aio/content/examples/router/src/app/heroes/hero-detail.component.1.ts rename to aio/content/examples/router/src/app/heroes/hero-detail/hero-detail.component.1.ts index 511b12d8a7..42cb60e1f8 100644 --- a/aio/content/examples/router/src/app/heroes/hero-detail.component.1.ts +++ b/aio/content/examples/router/src/app/heroes/hero-detail/hero-detail.component.1.ts @@ -9,24 +9,13 @@ import { Observable } from 'rxjs'; import { Router, ActivatedRoute, ParamMap } from '@angular/router'; // #enddocregion imports -import { Hero, HeroService } from './hero.service'; +import { HeroService } from '../hero.service'; +import { Hero } from '../hero'; @Component({ - template: ` -

HEROES

-
-

"{{ hero.name }}"

-
- {{ hero.id }}
-
- - -
-

- -

-
- ` + selector: 'app-hero-detail', + templateUrl: './hero-detail.component.html', + styleUrls: ['./hero-detail.component.css'] }) export class HeroDetailComponent implements OnInit { hero$: Observable; diff --git a/aio/content/examples/router/src/app/heroes/hero-detail.component.2.ts b/aio/content/examples/router/src/app/heroes/hero-detail/hero-detail.component.2.ts similarity index 61% rename from aio/content/examples/router/src/app/heroes/hero-detail.component.2.ts rename to aio/content/examples/router/src/app/heroes/hero-detail/hero-detail.component.2.ts index 0affccda9a..23d7e4fdc9 100644 --- a/aio/content/examples/router/src/app/heroes/hero-detail.component.2.ts +++ b/aio/content/examples/router/src/app/heroes/hero-detail/hero-detail.component.2.ts @@ -4,24 +4,13 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { Observable } from 'rxjs'; -import { Hero, HeroService } from './hero.service'; +import { HeroService } from '../hero.service'; +import { Hero } from '../hero'; @Component({ - template: ` -

HEROES

-
-

"{{ hero.name }}"

-
- {{ hero.id }}
-
- - -
-

- -

-
- ` + selector: 'app-hero-detaill', + templateUrl: './hero-detail.component.html', + styleUrls: ['./hero-detail.component.css'] }) export class HeroDetailComponent implements OnInit { hero$: Observable; diff --git a/aio/content/examples/router/src/app/heroes/hero-detail.component.ts b/aio/content/examples/router/src/app/heroes/hero-detail/hero-detail.component.3.ts similarity index 69% rename from aio/content/examples/router/src/app/heroes/hero-detail.component.ts rename to aio/content/examples/router/src/app/heroes/hero-detail/hero-detail.component.3.ts index 2ef9347dcf..52d670637e 100644 --- a/aio/content/examples/router/src/app/heroes/hero-detail.component.ts +++ b/aio/content/examples/router/src/app/heroes/hero-detail/hero-detail.component.3.ts @@ -3,28 +3,17 @@ // #docregion rxjs-operator-import import { switchMap } from 'rxjs/operators'; // #enddocregion rxjs-operator-import -import { Component, OnInit, HostBinding } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { Router, ActivatedRoute, ParamMap } from '@angular/router'; import { Observable } from 'rxjs'; -import { Hero, HeroService } from './hero.service'; +import { HeroService } from '../hero.service'; +import { Hero } from '../hero'; @Component({ - template: ` -

HEROES

-
-

"{{ hero.name }}"

-
- {{ hero.id }}
-
- - -
-

- -

-
- ` + selector: 'app-hero-detail', + templateUrl: './hero-detail.component.html', + styleUrls: ['./hero-detail.component.css'] }) export class HeroDetailComponent implements OnInit { hero$: Observable; @@ -56,3 +45,9 @@ export class HeroDetailComponent implements OnInit { } // #enddocregion gotoHeroes } + +/* +// #docregion redirect + this.router.navigate(['/superheroes', { id: heroId, foo: 'foo' }]); +// #enddocregion redirect +*/ diff --git a/aio/content/examples/router/src/app/heroes/hero-detail/hero-detail.component.css b/aio/content/examples/router/src/app/heroes/hero-detail/hero-detail.component.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/aio/content/examples/router/src/app/heroes/hero-detail/hero-detail.component.html b/aio/content/examples/router/src/app/heroes/hero-detail/hero-detail.component.html new file mode 100644 index 0000000000..52321987b4 --- /dev/null +++ b/aio/content/examples/router/src/app/heroes/hero-detail/hero-detail.component.html @@ -0,0 +1,13 @@ +

HEROES

+
+

"{{ hero.name }}"

+
+ {{ hero.id }}
+
+ + +
+

+ +

+
\ No newline at end of file diff --git a/aio/content/examples/router/src/app/heroes/hero-detail/hero-detail.component.ts b/aio/content/examples/router/src/app/heroes/hero-detail/hero-detail.component.ts new file mode 100644 index 0000000000..720301d192 --- /dev/null +++ b/aio/content/examples/router/src/app/heroes/hero-detail/hero-detail.component.ts @@ -0,0 +1,41 @@ +// #docplaster +// #docregion +import { switchMap } from 'rxjs/operators'; +import { Component, OnInit } from '@angular/core'; +import { Router, ActivatedRoute, ParamMap } from '@angular/router'; +import { Observable } from 'rxjs'; + +import { HeroService } from '../hero.service'; +import { Hero } from '../hero'; + +@Component({ + selector: 'app-hero-detail', + templateUrl: './hero-detail.component.html', + styleUrls: ['./hero-detail.component.css'] +}) +export class HeroDetailComponent implements OnInit { + hero$: Observable; + + constructor( + private route: ActivatedRoute, + private router: Router, + private service: HeroService + ) {} + + ngOnInit() { + this.hero$ = this.route.paramMap.pipe( + switchMap((params: ParamMap) => + this.service.getHero(params.get('id'))) + ); + } + + // #docregion redirect + gotoHeroes(hero: Hero) { + let heroId = hero ? hero.id : null; + // Pass along the hero id if available + // so that the HeroList component can select that hero. + // Include a junk 'foo' property for fun. + this.router.navigate(['/superheroes', { id: heroId, foo: 'foo' }]); + } + // #enddocregion redirect +} diff --git a/aio/content/examples/router/src/app/heroes/hero-list/hero-list.component.1.html b/aio/content/examples/router/src/app/heroes/hero-list/hero-list.component.1.html new file mode 100644 index 0000000000..579719d5f4 --- /dev/null +++ b/aio/content/examples/router/src/app/heroes/hero-list/hero-list.component.1.html @@ -0,0 +1,14 @@ + +

HEROES

+ + + + \ No newline at end of file diff --git a/aio/content/examples/router/src/app/heroes/hero-list.component.1.ts b/aio/content/examples/router/src/app/heroes/hero-list/hero-list.component.1.ts similarity index 57% rename from aio/content/examples/router/src/app/heroes/hero-list.component.1.ts rename to aio/content/examples/router/src/app/heroes/hero-list/hero-list.component.1.ts index 50ad9b74e1..c34e5ff678 100644 --- a/aio/content/examples/router/src/app/heroes/hero-list.component.1.ts +++ b/aio/content/examples/router/src/app/heroes/hero-list/hero-list.component.1.ts @@ -5,25 +5,13 @@ import { Component, OnInit } from '@angular/core'; import { Router } from '@angular/router'; import { Observable } from 'rxjs'; -import { Hero, HeroService } from './hero.service'; +import { HeroService } from '../hero.service'; +import { Hero } from '../hero'; @Component({ - // #docregion template - template: ` -

HEROES

- - - - ` - // #enddocregion template + selector: 'app-hero-list', + templateUrl: './hero-list.component.1.html', + styleUrls: ['./hero-list.component.css'] }) export class HeroListComponent implements OnInit { heroes$: Observable; diff --git a/aio/content/examples/router/src/app/heroes/hero-list/hero-list.component.css b/aio/content/examples/router/src/app/heroes/hero-list/hero-list.component.css new file mode 100644 index 0000000000..a5e62b257b --- /dev/null +++ b/aio/content/examples/router/src/app/heroes/hero-list/hero-list.component.css @@ -0,0 +1,82 @@ +/* HeroListComponent's private CSS styles */ +.heroes { + margin: 0 0 2em 0; + list-style-type: none; + padding: 0; + width: 15em; +} +.heroes li { + position: relative; + cursor: pointer; + background-color: #EEE; + margin: .5em; + padding: .3em 0; + height: 1.6em; + border-radius: 4px; +} + +.heroes li:hover { + color: #607D8B; + background-color: #DDD; + left: .1em; +} + +.heroes a { + color: #888; + text-decoration: none; + position: relative; + display: block; +} + +.heroes a:hover { + color:#607D8B; +} + +.heroes .badge { + display: inline-block; + font-size: small; + color: white; + padding: 0.8em 0.7em 0 0.7em; + background-color: #607D8B; + line-height: 1em; + position: relative; + left: -1px; + top: -4px; + height: 1.8em; + min-width: 16px; + text-align: right; + margin-right: .8em; + border-radius: 4px 0 0 4px; +} + +button { + background-color: #eee; + border: none; + padding: 5px 10px; + border-radius: 4px; + cursor: pointer; + cursor: hand; + font-family: Arial; +} + +button:hover { + background-color: #cfd8dc; +} + +button.delete { + position: relative; + left: 194px; + top: -32px; + background-color: gray !important; + color: white; +} + +/* #docregion selected */ +.heroes li.selected { + background-color: #CFD8DC; + color: white; +} +.heroes li.selected:hover { + background-color: #BBD8DC; +} +/* #enddocregion selected */ \ No newline at end of file diff --git a/aio/content/examples/router/src/app/heroes/hero-list/hero-list.component.html b/aio/content/examples/router/src/app/heroes/hero-list/hero-list.component.html new file mode 100644 index 0000000000..282a82e174 --- /dev/null +++ b/aio/content/examples/router/src/app/heroes/hero-list/hero-list.component.html @@ -0,0 +1,11 @@ +

HEROES

+ + + \ No newline at end of file diff --git a/aio/content/examples/router/src/app/heroes/hero-list.component.ts b/aio/content/examples/router/src/app/heroes/hero-list/hero-list.component.ts similarity index 57% rename from aio/content/examples/router/src/app/heroes/hero-list.component.ts rename to aio/content/examples/router/src/app/heroes/hero-list/hero-list.component.ts index cbb97c4cf0..b367f7b76b 100644 --- a/aio/content/examples/router/src/app/heroes/hero-list.component.ts +++ b/aio/content/examples/router/src/app/heroes/hero-list/hero-list.component.ts @@ -7,33 +7,21 @@ import { switchMap } from 'rxjs/operators'; // #enddocregion rxjs-imports import { Component, OnInit } from '@angular/core'; // #docregion import-router -import { ActivatedRoute, ParamMap } from '@angular/router'; +import { ActivatedRoute } from '@angular/router'; // #enddocregion import-router -import { Hero, HeroService } from './hero.service'; +import { HeroService } from '../hero.service'; +import { Hero } from '../hero'; @Component({ - // #docregion template - template: ` -

HEROES

- - - - ` - // #enddocregion template + selector: 'app-hero-list', + templateUrl: './hero-list.component.html', + styleUrls: ['./hero-list.component.css'] }) // #docregion ctor export class HeroListComponent implements OnInit { heroes$: Observable; - - private selectedId: number; + selectedId: number; constructor( private service: HeroService, @@ -42,7 +30,7 @@ export class HeroListComponent implements OnInit { ngOnInit() { this.heroes$ = this.route.paramMap.pipe( - switchMap((params: ParamMap) => { + switchMap(params => { // (+) before `params.get()` turns the string into a number this.selectedId = +params.get('id'); return this.service.getHeroes(); diff --git a/aio/content/examples/router/src/app/heroes/hero.service.ts b/aio/content/examples/router/src/app/heroes/hero.service.ts index 51518a4ae0..2a85f7511c 100644 --- a/aio/content/examples/router/src/app/heroes/hero.service.ts +++ b/aio/content/examples/router/src/app/heroes/hero.service.ts @@ -1,29 +1,31 @@ // #docregion import { Injectable } from '@angular/core'; -import { of } from 'rxjs'; + +import { Observable, of } from 'rxjs'; import { map } from 'rxjs/operators'; -export class Hero { - constructor(public id: number, public name: string) { } -} +import { Hero } from './hero'; +import { HEROES } from './mock-heroes'; +import { MessageService } from '../message.service'; -const HEROES = [ - new Hero(11, 'Mr. Nice'), - new Hero(12, 'Narco'), - new Hero(13, 'Bombasto'), - new Hero(14, 'Celeritas'), - new Hero(15, 'Magneta'), - new Hero(16, 'RubberMan') -]; - -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class HeroService { - getHeroes() { return of(HEROES); } + + constructor(private messageService: MessageService) { } + + getHeroes(): Observable { + // TODO: send the message _after_ fetching the heroes + this.messageService.add('HeroService: fetched heroes'); + return of(HEROES); + } getHero(id: number | string) { return this.getHeroes().pipe( // (+) before `id` turns the string into a number - map(heroes => heroes.find(hero => hero.id === +id)) + map((heroes: Hero[]) => heroes.find(hero => hero.id === +id)) ); } } + diff --git a/aio/content/examples/router/src/app/heroes/hero.ts b/aio/content/examples/router/src/app/heroes/hero.ts new file mode 100644 index 0000000000..e3eac516da --- /dev/null +++ b/aio/content/examples/router/src/app/heroes/hero.ts @@ -0,0 +1,4 @@ +export class Hero { + id: number; + name: string; +} diff --git a/aio/content/examples/router/src/app/heroes/heroes-routing.module.1.ts b/aio/content/examples/router/src/app/heroes/heroes-routing.module.1.ts index dbee521793..7a85522fb3 100644 --- a/aio/content/examples/router/src/app/heroes/heroes-routing.module.1.ts +++ b/aio/content/examples/router/src/app/heroes/heroes-routing.module.1.ts @@ -2,8 +2,8 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { HeroListComponent } from './hero-list.component'; -import { HeroDetailComponent } from './hero-detail.component'; +import { HeroListComponent } from './hero-list/hero-list.component'; +import { HeroDetailComponent } from './hero-detail/hero-detail.component'; const heroesRoutes: Routes = [ { path: 'heroes', component: HeroListComponent }, @@ -20,5 +20,5 @@ const heroesRoutes: Routes = [ RouterModule ] }) -export class HeroRoutingModule { } +export class HeroesRoutingModule { } // #enddocregion diff --git a/aio/content/examples/router/src/app/heroes/heroes-routing.module.2.ts b/aio/content/examples/router/src/app/heroes/heroes-routing.module.2.ts index 6f8d5fda4b..d9997c873e 100644 --- a/aio/content/examples/router/src/app/heroes/heroes-routing.module.2.ts +++ b/aio/content/examples/router/src/app/heroes/heroes-routing.module.2.ts @@ -2,8 +2,8 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { HeroListComponent } from './hero-list.component'; -import { HeroDetailComponent } from './hero-detail.component'; +import { HeroListComponent } from './hero-list/hero-list.component'; +import { HeroDetailComponent } from './hero-detail/hero-detail.component'; const heroesRoutes: Routes = [ { path: 'heroes', component: HeroListComponent, data: { animation: 'heroes' } }, @@ -18,5 +18,5 @@ const heroesRoutes: Routes = [ RouterModule ] }) -export class HeroRoutingModule { } +export class HeroesRoutingModule { } // #enddocregion diff --git a/aio/content/examples/router/src/app/heroes/heroes-routing.module.ts b/aio/content/examples/router/src/app/heroes/heroes-routing.module.ts index 11aab5f48c..b059e5d496 100644 --- a/aio/content/examples/router/src/app/heroes/heroes-routing.module.ts +++ b/aio/content/examples/router/src/app/heroes/heroes-routing.module.ts @@ -2,8 +2,8 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { HeroListComponent } from './hero-list.component'; -import { HeroDetailComponent } from './hero-detail.component'; +import { HeroListComponent } from './hero-list/hero-list.component'; +import { HeroDetailComponent } from './hero-detail/hero-detail.component'; const heroesRoutes: Routes = [ { path: 'heroes', redirectTo: '/superheroes' }, @@ -20,5 +20,5 @@ const heroesRoutes: Routes = [ RouterModule ] }) -export class HeroRoutingModule { } +export class HeroesRoutingModule { } // #enddocregion diff --git a/aio/content/examples/router/src/app/heroes/heroes.module.ts b/aio/content/examples/router/src/app/heroes/heroes.module.ts index 95ee64a182..13091398ab 100644 --- a/aio/content/examples/router/src/app/heroes/heroes.module.ts +++ b/aio/content/examples/router/src/app/heroes/heroes.module.ts @@ -5,13 +5,11 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; -import { HeroListComponent } from './hero-list.component'; -import { HeroDetailComponent } from './hero-detail.component'; - -import { HeroService } from './hero.service'; +import { HeroListComponent } from './hero-list/hero-list.component'; +import { HeroDetailComponent } from './hero-detail/hero-detail.component'; // #enddocregion v1 -import { HeroRoutingModule } from './heroes-routing.module'; +import { HeroesRoutingModule } from './heroes-routing.module'; // #docregion v1 @NgModule({ @@ -19,14 +17,13 @@ import { HeroRoutingModule } from './heroes-routing.module'; CommonModule, FormsModule, // #enddocregion v1 - HeroRoutingModule + HeroesRoutingModule // #docregion v1 ], declarations: [ HeroListComponent, HeroDetailComponent - ], - providers: [ HeroService ] + ] }) export class HeroesModule {} // #enddocregion v1 diff --git a/aio/content/examples/router/src/app/heroes/mock-heroes.ts b/aio/content/examples/router/src/app/heroes/mock-heroes.ts new file mode 100644 index 0000000000..e84c2fd2b0 --- /dev/null +++ b/aio/content/examples/router/src/app/heroes/mock-heroes.ts @@ -0,0 +1,14 @@ +import { Hero } from './hero'; + +export const HEROES: Hero[] = [ + { id: 11, name: 'Mr. Nice' }, + { id: 12, name: 'Narco' }, + { id: 13, name: 'Bombasto' }, + { id: 14, name: 'Celeritas' }, + { id: 15, name: 'Magneta' }, + { id: 16, name: 'RubberMan' }, + { id: 17, name: 'Dynama' }, + { id: 18, name: 'Dr IQ' }, + { id: 19, name: 'Magma' }, + { id: 20, name: 'Tornado' } +]; diff --git a/aio/content/examples/router/src/app/message.service.ts b/aio/content/examples/router/src/app/message.service.ts new file mode 100644 index 0000000000..d72412e115 --- /dev/null +++ b/aio/content/examples/router/src/app/message.service.ts @@ -0,0 +1,16 @@ +import { Injectable } from '@angular/core'; + +@Injectable({ + providedIn: 'root', +}) +export class MessageService { + messages: string[] = []; + + add(message: string) { + this.messages.push(message); + } + + clear() { + this.messages = []; + } +} diff --git a/aio/content/examples/router/src/app/not-found.component.ts b/aio/content/examples/router/src/app/not-found.component.ts deleted file mode 100644 index 2e74544e17..0000000000 --- a/aio/content/examples/router/src/app/not-found.component.ts +++ /dev/null @@ -1,7 +0,0 @@ -// #docregion -import { Component } from '@angular/core'; - -@Component({ - template: '

Page not found

' -}) -export class PageNotFoundComponent {} diff --git a/aio/content/examples/router/src/app/page-not-found/page-not-found.component.css b/aio/content/examples/router/src/app/page-not-found/page-not-found.component.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/aio/content/examples/router/src/app/page-not-found/page-not-found.component.html b/aio/content/examples/router/src/app/page-not-found/page-not-found.component.html new file mode 100644 index 0000000000..6c581c4fc8 --- /dev/null +++ b/aio/content/examples/router/src/app/page-not-found/page-not-found.component.html @@ -0,0 +1 @@ +

Page not found

\ No newline at end of file diff --git a/aio/content/examples/router/src/app/page-not-found/page-not-found.component.spec.ts b/aio/content/examples/router/src/app/page-not-found/page-not-found.component.spec.ts new file mode 100644 index 0000000000..697a946572 --- /dev/null +++ b/aio/content/examples/router/src/app/page-not-found/page-not-found.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { PageNotFoundComponent } from './page-not-found.component'; + +describe('PageNotFoundComponent', () => { + let component: PageNotFoundComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ PageNotFoundComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(PageNotFoundComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/aio/content/examples/router/src/app/page-not-found/page-not-found.component.ts b/aio/content/examples/router/src/app/page-not-found/page-not-found.component.ts new file mode 100644 index 0000000000..c5c55a794d --- /dev/null +++ b/aio/content/examples/router/src/app/page-not-found/page-not-found.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-page-not-found', + templateUrl: './page-not-found.component.html', + styleUrls: ['./page-not-found.component.css'] +}) +export class PageNotFoundComponent implements OnInit { + + constructor() { } + + ngOnInit() { + } + +} diff --git a/aio/content/examples/router/src/app/selective-preloading-strategy.ts b/aio/content/examples/router/src/app/selective-preloading-strategy.service.ts similarity index 83% rename from aio/content/examples/router/src/app/selective-preloading-strategy.ts rename to aio/content/examples/router/src/app/selective-preloading-strategy.service.ts index c2192ec12b..b1065b78af 100644 --- a/aio/content/examples/router/src/app/selective-preloading-strategy.ts +++ b/aio/content/examples/router/src/app/selective-preloading-strategy.service.ts @@ -3,8 +3,10 @@ import { Injectable } from '@angular/core'; import { PreloadingStrategy, Route } from '@angular/router'; import { Observable, of } from 'rxjs'; -@Injectable() -export class SelectivePreloadingStrategy implements PreloadingStrategy { +@Injectable({ + providedIn: 'root' +}) +export class SelectivePreloadingStrategyService implements PreloadingStrategy { preloadedModules: string[] = []; preload(route: Route, load: () => Observable): Observable { diff --git a/aio/content/examples/router/src/index.html b/aio/content/examples/router/src/index.html index 0e5ca721fa..edb919d1ec 100644 --- a/aio/content/examples/router/src/index.html +++ b/aio/content/examples/router/src/index.html @@ -9,7 +9,6 @@ Angular Router - diff --git a/aio/content/examples/router/stackblitz.json b/aio/content/examples/router/stackblitz.json index c1f330ae39..4ffcac610b 100644 --- a/aio/content/examples/router/stackblitz.json +++ b/aio/content/examples/router/stackblitz.json @@ -4,8 +4,8 @@ "!**/*.d.ts", "!**/*.js", "!**/*.[0-9].*", - "!src/app/crisis-list.component.ts", - "!src/app/hero-list.component.ts" + "!src/app/crisis-list/*.*", + "!src/app/hero-list/*.*" ], "tags": ["router"] } diff --git a/aio/content/guide/router.md b/aio/content/guide/router.md index 3802e1eb9f..8bd3bedd62 100644 --- a/aio/content/guide/router.md +++ b/aio/content/guide/router.md @@ -150,18 +150,20 @@ If you need to see what events are happening during the navigation lifecycle, th Given this configuration, when the browser URL for this application becomes `/heroes`, the router matches that URL to the route path `/heroes` and displays the `HeroListComponent` -_after_ a `RouterOutlet` that you've placed in the host view's HTML. +_after_ a `RouterOutlet` that you've placed in the host component's template. + +The `RouterOutlet` is a directive from the router library that marks +the spot in the template where the router should display the components for that outlet. <router-outlet></router-outlet> - <!-- Routed views go here --> + <!-- Routed components go here --> - - {@a basics-router-links} +{@a router-link} ### Router links @@ -174,12 +176,10 @@ an anchor tag. Consider the following template: - + - - The `RouterLink` directives on the anchor tags give the router control over those elements. The navigation paths are fixed, so you can assign a string to the `routerLink` (a "one-time" binding). @@ -187,9 +187,25 @@ Had the navigation path been more dynamic, you could have bound to a template ex returned an array of route link parameters (the _link parameters array_). The router resolves that array into a complete URL. + +{@a router-link-active} + + +### Active Router Links + The **`RouterLinkActive`** directive on each anchor tag helps visually distinguish the anchor for the currently selected "active" route. -The router adds the `active` CSS class to the element when the associated *RouterLink* becomes active. -You can add this directive to the anchor or to its parent element. + +On each anchor tag, you also see [property bindings](guide/template-syntax#property-binding) to the `RouterLinkActive` directive that look like `routerLinkActive="..."`. + +The template expression to the right of the equals (=) contains a space-delimited string of CSS classes +that the Router will add when this link is active (and remove when the link is inactive). +You can also set the `RouterLinkActive` directive to a string of classes such as `[routerLinkActive]="'active fluffy'"` +or bind it to a component property that returns such a string. + +The `RouterLinkActive` directive toggles css classes for active `RouterLink`s based on the current `RouterState`. +This cascades down through each level of the route tree, so parent and child router links can be active at the same time. +To override this behavior, you can bind to the `[routerLinkActiveOptions]` input binding with the `{ exact: true }` expression. +By using `{ exact: true }`, a given `RouterLink` will only be active if its URL is an exact match to the current URL. {@a basics-router-state} @@ -373,17 +389,6 @@ During each navigation, the `Router` emits navigation events through the `Router - - - RoutesRecognized - - - - An [event](api/router/RoutesRecognized) triggered when the Router parses the URL and the routes are recognized. - - - - RouteConfigLoadStart @@ -407,6 +412,85 @@ During each navigation, the `Router` emits navigation events through the `Router + + + RoutesRecognized + + + + An [event](api/router/RoutesRecognized) triggered when the Router parses the URL and the routes are recognized. + + + + + + + GuardsCheckStart + + + + An [event](api/router/GuardsCheckStart) triggered when the Router begins the Guards phase of routing. + + + + + + + ChildActivationStart + + + + An [event](api/router/ChildActivationStart) triggered when the Router begins activating a route's children. This + event receives the Route config for the parent route. + + + + + + + GuardsCheckEnd + + + + An [event](api/router/GuardsCheckEnd) triggered when the Router finishes the Guards phase of routing successfully. + + + + + + + ResolveStart + + + + An [event](api/router/ResolveStart) triggered when the Router begins the Resolve phase of routing. + + + + + + + ResolveEnd + + + + An [event](api/router/ResolveEnd) triggered when the Router finishes the Resolve phase of routing successfuly. + + + + + + + ChildActivationEnd + + + + An [event](api/router/ChildActivationEnd) triggered when the Router finishes activating a route's children. This + event receives the Route config for the parent route. + + + + NavigationEnd @@ -440,9 +524,20 @@ During each navigation, the `Router` emits navigation events through the `Router + + + + Scroll + + + + An [event](api/router/Scroll) that represents a scrolling event. + + + -These events are logged to the console when the `enableTracing` option is enabled also. Since the events are provided as an `Observable`, you can `filter()` for events of interest and `subscribe()` to them to make decisions based on the sequence of events in the navigation process. +These events are logged to the console when the `enableTracing` option is enabled also. Because the events are provided as an `Observable`, you can `filter()` for events of interest and `subscribe()` to them to make decisions based on the sequence of events in the navigation process. {@a basics-summary} @@ -692,7 +787,7 @@ Now click the *Crisis Center* link for a list of ongoing crises. Select a crisis and the application takes you to a crisis editing screen. -The _Crisis Detail_ appears in a child view on the same page, beneath the list. +The _Crisis Detail_ appears in a child component on the same page, beneath the list. Alter the name of a crisis. Notice that the corresponding name in the crisis list does _not_ change. @@ -732,112 +827,27 @@ Proceed to the first application milestone. {@a getting-started} -## Milestone 1: Getting started with the router +## Milestone 1: Getting started Begin with a simple version of the app that navigates between two empty views. +
App in action
- - -{@a base-href} - - -### Set the *<base href>* - -The router uses the browser's -history.pushState -for navigation. Thanks to `pushState`, you can make in-app URL paths look the way you want them to -look, e.g. `localhost:3000/crisis-center`. The in-app URLs can be indistinguishable from server URLs. - -Modern HTML5 browsers were the first to support `pushState` which is why many people refer to these URLs as -"HTML5 style" URLs. - - -
- - - -HTML5 style navigation is the router default. -In the [LocationStrategy and browser URL styles](#browser-url-styles) Appendix, -learn why HTML5 style is preferred, how to adjust its behavior, and how to switch to the -older hash (#) style, if necessary. - - -
- - - -You must **add a -<base href> element** -to the app's `index.html` for `pushState` routing to work. -The browser uses the `` value to prefix *relative* URLs when referencing -CSS files, scripts, and images. - -Add the `` element just after the `` tag. -If the `app` folder is the application root, as it is for this application, -set the `href` value in **`index.html`** *exactly* as shown here. - - - - - - - - -
- - - -
- Live example note -
- - - -A live coding environment like Stackblitz sets the application base address dynamically so you can't specify a fixed address. -That's why the example code replaces the `` with a script that writes the `` tag on the fly. - - - - <script>document.write('<base href="' + document.location + '" />');</script> - - - - - -You only need this trick for the live example, not production code. - - -
- - - {@a import} +Generate a sample application to follow the walkthrough. -### Importing from the router library + + ng new angular-router-sample + -Begin by importing some symbols from the router library. -The Router is in its own `@angular/router` package. -It's not part of the Angular core. The router is an optional service because not all applications -need routing and, depending on your requirements, you may need a different routing library. - -You teach the router how to navigate by configuring it with routes. - - -{@a route-config} - - -#### Define routes +### Define Routes A router must be configured with a list of route definitions. -The first configuration defines an array of two routes with simple paths leading to the -`CrisisListComponent` and `HeroListComponent`. - Each definition translates to a [Route](api/router/Route) object which has two things: a `path`, the URL path segment for this route; and a `component`, the component associated with this route. @@ -854,155 +864,75 @@ the router activates an instance of the `CrisisListComponent` and displays its v activates an instance of `CrisisListComponent`, displays its view, and updates the browser's address location and history with the URL for that path. +The first configuration defines an array of two routes with simple paths leading to the +`CrisisListComponent` and `HeroListComponent`. Generate the `CrisisList` and `HeroList` components. -Here is the first configuration. Pass the array of routes, `appRoutes`, to the `RouterModule.forRoot` method. -It returns a module, containing the configured `Router` service provider, plus other providers that the routing library requires. -Once the application is bootstrapped, the `Router` performs the initial navigation based on the current browser URL. + + ng generate component crisis-list + + + + ng generate component hero-list + + +Replace the contents of each component with the sample HTML below. + + + + + + + + + + + + + +### Register Router and Routes + +In order to use the Router, you must first register the `RouterModule` from the `@angular/router` package. Define an array of routes, `appRoutes`, and pass them to the `RouterModule.forRoot` method. It returns a module, containing the configured `Router` service provider, plus other providers that the routing library requires. Once the application is bootstrapped, the `Router` performs the initial navigation based on the current browser URL. - -
- - -Adding the configured `RouterModule` to the `AppModule` is sufficient for simple route configurations. -As the application grows, you'll want to refactor the routing configuration into a separate file -and create a **[Routing Module](#routing-module)**, a special type of `Service Module` dedicated to the purpose -of routing in feature modules. - +Adding the configured `RouterModule` to the `AppModule` is sufficient for simple route configurations. As the application grows, you'll want to refactor the routing configuration into a separate file and create a **[Routing Module](#routing-module)**, a special type of `Service Module` dedicated to the purpose of routing in feature modules.
- - Providing the `RouterModule` in the `AppModule` makes the Router available everywhere in the application. - {@a shell} -### The *AppComponent* shell +### Add the Router Outlet -The root `AppComponent` is the application shell. It has a title, a navigation bar with two links, -and a *router outlet* where the router swaps views on and off the page. Here's what you get: +The root `AppComponent` is the application shell. It has a title, a navigation bar with two links, and a *router outlet* where the router swaps views on and off the page. Here's what you get:
Shell
- +The `router-outlet` serves as a placeholder when the routed components will be rendered below it. {@a shell-template} - The corresponding component template looks like this: - - + - - -{@a router-outlet} - - -### *RouterOutlet* - -The `RouterOutlet` is a directive from the router library that marks -the spot in the template where the router should display the views for that outlet. - - -
- - - -The router adds the `` element to the DOM -and subsequently inserts the navigated view element -immediately _after_ the ``. - - -
- - - -{@a router-link} - - -### *RouterLink* binding - -Above the outlet, within the anchor tags, you see -[attribute bindings](guide/template-syntax#attribute-binding) to -the `RouterLink` directive that look like `routerLink="..."`. - -The links in this example each have a string path, the path of a route that -you configured earlier. There are no route parameters yet. - -You can also add more contextual information to the `RouterLink` by providing query string parameters -or a URL fragment for jumping to different areas on the page. Query string parameters -are provided through the `[queryParams]` binding which takes an object (e.g. `{ name: 'value' }`), while the URL fragment -takes a single value bound to the `[fragment]` input binding. - -
- - - -Learn about the how you can also use the _link parameters array_ in the [appendix below](#link-parameters-array). - - -
- - - -{@a router-link-active} - - -### *RouterLinkActive* binding - -On each anchor tag, you also see [property bindings](guide/template-syntax#property-binding) to -the `RouterLinkActive` directive that look like `routerLinkActive="..."`. - -The template expression to the right of the equals (=) contains a space-delimited string of CSS classes -that the Router will add when this link is active (and remove when the link is inactive). -You can also set the `RouterLinkActive` directive to a string of classes such as `[routerLinkActive]="'active fluffy'"` -or bind it to a component property that returns such a string. - -The `RouterLinkActive` directive toggles css classes for active `RouterLink`s based on the current `RouterState`. -This cascades down through each level of the route tree, so parent and child router links can be active at the same time. -To override this behavior, you can bind to the `[routerLinkActiveOptions]` input binding with the `{ exact: true }` expression. -By using `{ exact: true }`, a given `RouterLink` will only be active if its URL is an exact match to the current URL. - - -{@a router-directives} - - -### *Router directives* - -`RouterLink`, `RouterLinkActive` and `RouterOutlet` are directives provided by the Angular `RouterModule` package. -They are readily available for you to use in the template. - -The current state of `app.component.ts` looks like this: - - - - - - - - {@a wildcard} +### Define a Wildcard route -### Wildcard route - -You've created two routes in the app so far, one to `/crisis-center` and the other to `/heroes`. -Any other URL causes the router to throw an error and crash the app. +You've created two routes in the app so far, one to `/crisis-center` and the other to `/heroes`. Any other URL causes the router to throw an error and crash the app. Add a **wildcard** route to intercept invalid URLs and handle them gracefully. A _wildcard_ route has a path consisting of two asterisks. It matches _every_ URL. @@ -1012,25 +942,18 @@ A wildcard route can navigate to a custom "404 Not Found" component or [redirect
- - The router selects the route with a [_first match wins_](#example-config) strategy. Wildcard routes are the least specific routes in the route configuration. Be sure it is the _last_ route in the configuration. -
- - To test this feature, add a button with a `RouterLink` to the `HeroListComponent` template and set the link to `"/sidekicks"`. - + - - The application will fail if the user clicks that button because you haven't defined a `"/sidekicks"` route yet. Instead of adding the `"/sidekicks"` route, define a `wildcard` route instead and have it navigate to a simple `PageNotFoundComponent`. @@ -1039,32 +962,27 @@ Instead of adding the `"/sidekicks"` route, define a `wildcard` route instead an - - Create the `PageNotFoundComponent` to display when users visit invalid URLs. - - + + ng generate component page-not-found + - -As with the other components, add the `PageNotFoundComponent` to the `AppModule` declarations. + Now when the user visits `/sidekicks`, or any other invalid URL, the browser displays "Page not found". The browser address bar continues to point to the invalid URL. +{@a redirect} - -{@a default-route} - - -### The _default_ route to heroes +### Set up redirects When the application launches, the initial URL in the browser bar is something like: - localhost:3000 + localhost:4200 That doesn't match any of the concrete configured routes which means @@ -1072,11 +990,7 @@ the router falls through to the wildcard route and displays the `PageNotFoundCom The application needs a **default route** to a valid page. The default page for this app is the list of heroes. -The app should navigate there as if the user clicked the "Heroes" link or pasted `localhost:3000/heroes` into the address bar. - -{@a redirect} - -### Redirecting routes +The app should navigate there as if the user clicked the "Heroes" link or pasted `localhost:4200/heroes` into the address bar. The preferred solution is to add a `redirect` route that translates the initial relative URL (`''`) to the desired default path (`/heroes`). The browser address bar shows `.../heroes` as if you'd navigated there directly. @@ -1089,7 +1003,6 @@ It's just above the wildcard route in the following excerpt showing the complete - A redirect route requires a `pathMatch` property to tell the router how to match a URL to the path of a route. The router throws an error if you don't. In this app, the router should select the route to the `HeroListComponent` only when the *entire URL* matches `''`, @@ -1099,7 +1012,6 @@ so set the `pathMatch` value to `'full'`.
- Technically, `pathMatch = 'full'` results in a route hit when the *remaining*, unmatched segments of the URL match `''`. In this example, the redirect is in a top level route so the *remaining* URL and the *entire* URL are the same thing. @@ -1127,7 +1039,6 @@ Learn more in Victor Savkin's
- ### Basics wrap up You've got a very basic navigating app, one that can switch between two views @@ -1143,16 +1054,12 @@ You've learned how to do the following: * handle invalid routes with a `wildcard` route. * navigate to the default route when the app launches with an empty path. -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. - The starter app's structure looks like this: -
- router-sample + angular-router-sample
@@ -1169,6 +1076,92 @@ The starter app's structure looks like this:
+
+ crisis-list +
+ +
+ +
+ + crisis-list.component.css + +
+ +
+ + crisis-list.component.html + +
+ +
+ + crisis-list.component.ts + +
+ +
+ +
+ hero-list +
+ +
+ +
+ + hero-list.component.css + +
+ +
+ + hero-list.component.html + +
+ +
+ + hero-list.component.ts + +
+ +
+ +
+ page-not-found +
+ +
+ +
+ + page-not-found.component.css + +
+ +
+ + page-not-found.component.html + +
+ +
+ + page-not-found.component.ts + +
+ +
+ +
+ app.component.css +
+ +
+ app.component.html +
+
app.component.ts
@@ -1177,18 +1170,6 @@ The starter app's structure looks like this: app.module.ts
-
- crisis-list.component.ts -
- -
- hero-list.component.ts -
- -
- not-found.component.ts -
-
@@ -1228,7 +1209,7 @@ Here are the files discussed in this milestone. - + @@ -1236,19 +1217,15 @@ Here are the files discussed in this milestone. - + - + - - - - - + @@ -1282,18 +1259,18 @@ The **Routing Module** has several characteristics: ### Refactor the routing configuration into a _routing module_ -Create a file named `app-routing.module.ts` in the `/app` folder to contain the routing module. +Create an `AppRouting` module in the `/app` folder to contain the routing configuration. -Import the `CrisisListComponent` and the `HeroListComponent` components + + ng generate module app-routing --module app --flat + + +Import the `CrisisListComponent`, `HeroListComponent`, and `PageNotFoundCompponent` symbols just like you did in the `app.module.ts`. Then move the `Router` imports and routing configuration, including `RouterModule.forRoot`, into this routing module. -Following convention, add a class name `AppRoutingModule` and export it -so you can import it later in `AppModule`. - -Finally, re-export the Angular `RouterModule` by adding it to the module `exports` array. -By re-exporting the `RouterModule` here and importing `AppRoutingModule` in `AppModule`, -the components declared in `AppModule` will have access to router directives such as `RouterLink` and `RouterOutlet`. +Re-export the Angular `RouterModule` by adding it to the module `exports` array. +By re-exporting the `RouterModule` here the components declared in `AppModule` will have access to router directives such as `RouterLink` and `RouterOutlet`. After these steps, the file should look like this. @@ -1301,11 +1278,8 @@ After these steps, the file should look like this. - - -Next, update the `app.module.ts` file, -first importing the newly created `AppRoutingModule`from `app-routing.module.ts`, -then replacing `RouterModule.forRoot` in the `imports` array with the `AppRoutingModule`. +Next, update the `app.module.ts` file, removing `RouterModule.forRoot` in +the `imports` array. @@ -1317,7 +1291,7 @@ then replacing `RouterModule.forRoot` in the `imports` array with the `AppRoutin -Later in this guide you will create [multiple routing modules](#hero-routing-module) and discover that +Later in this guide you will create [multiple routing modules](#heroes-functionality) and discover that you must import those routing modules [in the correct order](#routing-module-order). @@ -1395,24 +1369,40 @@ Then you'll import into the main module and navigate among them. Follow these steps: -* Create the `src/app/heroes` folder; you'll be adding files implementing *hero management* there. -* Delete the placeholder `hero-list.component.ts` that's in the `app` folder. -* Create a new `hero-list.component.ts` under `src/app/heroes`. -* Copy into it the contents of the `app.component.ts` from - the "Services" tutorial. -* Make a few minor but necessary changes: +* Create a `HeroesModule` with routing in the heroes folder and register it with the root `AppModule`. This is where you'll be implementing the *hero management*. + + + ng generate module heroes/heroes --module app --flat --routing + + +* Move the placeholder `hero-list` folder that's in the `app` into the `heroes` folder. +* Copy the contents of the `heroes/heroes.component.html` from + the "Services" tutorial into the `hero-list.component.html` template. - * Delete the `selector` (routed components don't need them). - * Delete the `

`. * Relabel the `

` to `

HEROES

`. - * Delete the `` at the bottom of the template. - * Rename the `AppComponent` class to `HeroListComponent`. + * Delete the `` component at the bottom of the template. -* Copy the `hero-detail.component.ts` and the `hero.service.ts` files into the `heroes` subfolder. -* Create a (pre-routing) `heroes.module.ts` in the heroes folder that looks like this: +* Copy the contents of the `heroes/heroes.component.css` from the live example into the `hero-list.component.css` file. +* Copy the contents of the `heroes/heroes.component.ts` from the live example into the `hero-list.component.ts` file. + * Change the component class name to `HeroListComponent`. + * Change the `selector` to `app-hero-list`. + +
- + Selectors are **not required** for _routed components_ due to the components are dynamically inserted when the page is rendered, but are useful for identifying and targeting them in your HTML element tree. + +
+ +* Copy the `hero-detail` folder, the `hero.ts`, `hero.service.ts`, and `mock-heroes.ts` files into the `heroes` subfolder. +* Copy the `message.service.ts` into the `src/app` folder. +* Update the relative path import to the `message.service` in the `hero.service.ts` file. + +Next, you'll update the `HeroesModule` metadata. + + * Import and add the `HeroDetailComponent` and `HeroListComponent` to the `declarations` array in the `HeroesModule`. + + @@ -1430,21 +1420,70 @@ When you're done, you'll have these *hero management* files:
- hero-detail.component.ts + hero-detail
+
+ +
+ hero-detail.component.css +
+ +
+ hero-detail.component.html +
+ +
+ hero-detail.component.ts +
+ +
+
- hero-list.component.ts + hero-list
+
+ +
+ hero-list.component.css +
+ +
+ hero-list.component.html +
+ +
+ hero-list.component.ts +
+ +
+
hero.service.ts +
+ +
+ hero.ts
+
+ heroes-routing.module.ts +
+
heroes.module.ts
+
+ mock-heroes.ts +
+ +
+ + + +
@@ -1454,7 +1493,7 @@ When you're done, you'll have these *hero management* files: {@a hero-routing-requirements} -### *Hero* feature routing requirements +#### *Hero* feature routing requirements The heroes feature has two interacting components, the hero list and the hero detail. The list view is self-sufficient; you navigate to it, it gets a list of heroes and displays them. @@ -1466,40 +1505,7 @@ When the user selects a hero from the list, the app should navigate to the detai and show that hero. You tell the detail view which hero to display by including the selected hero's id in the route URL. - -{@a hero-routing-module} - - -### *Hero* feature route configuration - -Create a new `heroes-routing.module.ts` in the `heroes` folder -using the same techniques you learned while creating the `AppRoutingModule`. - - - - - - - - -
- - - -Put the routing module file in the same folder as its companion module file. -Here both `heroes-routing.module.ts` and `heroes.module.ts` are in the same `src/app/heroes` folder. - -Consider giving each feature module its own route configuration file. -It may seem like overkill early when the feature routes are simple. -But routes have a tendency to grow more complex and consistency in patterns pays off over time. - - -
- - - -Import the hero components from their new locations in the `src/app/heroes/` folder, define the two hero routes, -and export the `HeroRoutingModule` class. +Import the hero components from their new locations in the `src/app/heroes/` folder, define the two hero routes. Now that you have routes for the `Heroes` module, register them with the `Router` via the `RouterModule` _almost_ as you did in the `AppRoutingModule`. @@ -1519,31 +1525,30 @@ In any other module, you must call the **`RouterModule.forChild`** method to reg +The updated `HeroesRoutingModule` looks like this: -{@a adding-routing-module} - - -### Add the routing module to the _HeroesModule_ -Add the `HeroRoutingModule` to the `HeroModule` -just as you added `AppRoutingModule` to the `AppModule`. - -Open `heroes.module.ts`. -Import the `HeroRoutingModule` token from `heroes-routing.module.ts` and -add it to the `imports` array of the `HeroesModule`. -The finished `HeroesModule` looks like this: - - - + +
+ + +Consider giving each feature module its own route configuration file. +It may seem like overkill early when the feature routes are simple. +But routes have a tendency to grow more complex and consistency in patterns pays off over time. + + +
+ + {@a remove-duplicate-hero-routes} -### Remove duplicate hero routes +#### Remove duplicate hero routes The hero routes are currently defined in _two_ places: in the `HeroesRoutingModule`, by way of the `HeroesModule`, and in the `AppRoutingModule`. @@ -1551,7 +1556,6 @@ by way of the `HeroesModule`, and in the `AppRoutingModule`. Routes provided by feature modules are combined together into their imported module's routes by the router. This allows you to continue defining the feature module routes without modifying the main route configuration. -But you don't want to define the same routes twice. Remove the `HeroListComponent` import and the `/heroes` route from the `app-routing.module.ts`. **Leave the default and the wildcard routes!** @@ -1567,20 +1571,9 @@ These are concerns at the top level of the application itself. {@a merge-hero-routes} -### Import hero module into AppModule -The heroes feature module is ready, but the application doesn't know about the `HeroesModule` yet. -Open `app.module.ts` and revise it as follows. +#### Remove heroes declarations -Import the `HeroesModule` and add it to the `imports` array in the `@NgModule` metadata of the `AppModule`. - -Remove the `HeroListComponent` from the `AppModule`'s `declarations` because it's now provided by the `HeroesModule`. -This is important. There can be only _one_ owner for a declared component. -In this case, the `Heroes` module is the owner of the `Heroes` components and is making them available to -components in the `AppModule` via the `HeroesModule`. - -As a result, the `AppModule` no longer has specific knowledge of the hero feature, its components, or its route details. -You can evolve the hero feature with more components and different routes. -That's a key benefit of creating a separate module for each feature area. +Remove the `HeroListComponent` from the `AppModule`'s `declarations` because it's now provided by the `HeroesModule`. You can evolve the hero feature with more components and different routes. That's a key benefit of creating a separate module for each feature area. After these steps, the `AppModule` should look like this: @@ -1634,12 +1627,12 @@ Learn about inspecting the runtime router configuration - +### Route Parameters {@a route-def-with-parameter} -### Route definition with a parameter +#### Route definition with a parameter Return to the `HeroesRoutingModule` and look at the route definitions again. The route to `HeroDetailComponent` has a twist. @@ -1659,7 +1652,7 @@ you expect a hero id to appear in the browser URL like this: - localhost:3000/hero/15 + localhost:4200/hero/15 @@ -1692,7 +1685,7 @@ a route for some other hero. {@a route-parameters} -### Setting the route parameters in the list view +#### Setting the route parameters in the list view After navigating to the `HeroDetailComponent`, you expect to see the details of the selected hero. You need *two* pieces of information: the routing path to the component and the hero's `id`. @@ -1701,14 +1694,14 @@ Accordingly, the _link parameters array_ has *two* items: the routing _path_ an `id` of the selected hero. - + The router composes the destination URL from the array like this: -`localhost:3000/hero/15`. +`localhost:4200/hero/15`. @@ -1732,7 +1725,7 @@ the `HeroDetailComponent` via the `ActivatedRoute` service. Import the `Router`, `ActivatedRoute`, and `ParamMap` tokens from the router package. - + @@ -1741,7 +1734,7 @@ Import the `Router`, `ActivatedRoute`, and `ParamMap` tokens from the router pac Import the `switchMap` operator because you need it later to process the `Observable` route parameters. - + @@ -1754,7 +1747,7 @@ As usual, you write a constructor that asks Angular to inject services that the component requires and reference them as private variables. - + @@ -1762,7 +1755,7 @@ Later, in the `ngOnInit` method, you use the `ActivatedRoute` service to retriev pull the hero `id` from the parameters and retrieve the hero to display. - + @@ -1901,7 +1894,7 @@ You can access the parameters directly without subscribing or adding observable It's much simpler to write and read: - + @@ -1934,28 +1927,28 @@ that you can bind to a `[routerLink]` directive. It holds the _path to the `HeroListComponent`_: - + {@a optional-route-parameters} -### Route Parameters: Required or optional? +#### Route Parameters: Required or optional? Use [*route parameters*](#route-parameters) to specify a *required* parameter value *within* the route URL as you do when navigating to the `HeroDetailComponent` in order to view the hero with *id* 15: - localhost:3000/hero/15 + localhost:4200/hero/15 You can also add *optional* information to a route request. -For example, when returning to the heroes list from the hero detail view, +For example, when returning to the hero-detail.component.ts list from the hero detail view, it would be nice if the viewed hero was preselected in the list. @@ -1990,13 +1983,13 @@ prefer an *optional parameter* when the value is optional, complex, and/or multi {@a optionally-selecting} -### Heroes list: optionally selecting a hero +#### Heroes list: optionally selecting a hero When navigating to the `HeroDetailComponent` you specified the _required_ `id` of the hero-to-edit in the *route parameter* and made it the second item of the [_link parameters array_](#link-parameters-array). - + @@ -2016,7 +2009,7 @@ When the user clicks the back button, the `HeroDetailComponent` constructs anoth which it uses to navigate back to the `HeroListComponent`. - + @@ -2033,7 +2026,7 @@ For demonstration purposes, there's an extra junk parameter (`foo`) in the objec Here's the revised navigation statement: - + @@ -2048,7 +2041,7 @@ It should look something like this, depending on where you run it: - localhost:3000/heroes;id=15;foo=foo + localhost:4200/heroes;id=15;foo=foo @@ -2115,7 +2108,7 @@ This time you'll be navigating in the opposite direction, from the `HeroDetailCo First you extend the router import statement to include the `ActivatedRoute` service symbol: - + @@ -2124,7 +2117,7 @@ First you extend the router import statement to include the `ActivatedRoute` ser Import the `switchMap` operator to perform an operation on the `Observable` of route parameter map. - + @@ -2133,7 +2126,7 @@ Import the `switchMap` operator to perform an operation on the `Observable` of r Then you inject the `ActivatedRoute` in the `HeroListComponent` constructor. - + @@ -2148,7 +2141,13 @@ The binding adds the `selected` CSS class when the comparison returns `true` and Look for it within the repeated `
  • ` tag as shown here: - + + + + +Add some styles to apply when the list item is selected. + + @@ -2164,16 +2163,17 @@ When the user navigates from the heroes list to the "Magneta" hero and back, "Ma The optional `foo` route parameter is harmless and continues to be ignored. +### Adding routable animations {@a route-animation} -### Adding animations to the routed component +#### Adding animations to the routed component The heroes feature module is almost complete, but what is a feature without some smooth transitions? This section shows you how to add some [animations](guide/animations) to the `HeroDetailComponent`. -First import `BrowserAnimationsModule`: +First import the `BrowserAnimationsModule` and add it to the `imports` array: @@ -2203,9 +2203,12 @@ This file does the following: You could also create more transitions for other routes. This trigger is sufficient for the current milestone. -Back in the `AppComponent`, import the `RouterOutlet` token from the `@angular/router` package and the `slideInDownAnimation` from `'./animations.ts`. +Back in the `AppComponent`, import the `RouterOutlet` token from the `@angular/router` package and the `slideInDownAnimation` from +`'./animations.ts`. - +Add an `animations` array to the `@Component` metadata's that contains the `slideInDownAnimation`. + + @@ -2214,9 +2217,7 @@ use the `@routeAnimation` trigger and bind it to the element. For the `@routeAnimation` transitions to key off states, you'll need to provide it with the `data` from the `ActivatedRoute`. The `RouterOutlet` is exposed as an `outlet` template variable, so you bind a reference to the router outlet. A variable of `routerOutlet` is an ideal choice. -Add an `animations` array to the `@Component` metadata's that contains the `slideInDownAnimation`. - - + @@ -2249,7 +2250,7 @@ After these changes, the folder structure looks like this:
    - router-sample + angular-router-sample
    @@ -2266,6 +2267,26 @@ After these changes, the folder structure looks like this:
    +
    + crisis-list +
    + +
    + +
    + crisis-list.component.css +
    + +
    + crisis-list.component.html +
    + +
    + crisis-list.component.ts +
    + +
    +
    heroes
    @@ -2273,49 +2294,127 @@ After these changes, the folder structure looks like this:
    - hero-detail.component.ts + hero-detail
    +
    + +
    + hero-detail.component.css +
    + +
    + hero-detail.component.html +
    + +
    + hero-detail.component.ts +
    + +
    +
    - hero-list.component.ts + hero-list
    +
    + +
    + hero-list.component.css +
    + +
    + hero-list.component.html +
    + +
    + hero-list.component.ts +
    + +
    +
    hero.service.ts +
    + +
    + hero.ts
    +
    + heroes-routing.module.ts +
    +
    heroes.module.ts
    - heroes-routing.module.ts + mock-heroes.ts
    - app.component.ts + page-not-found
    -
    - app.module.ts -
    +
    -
    - app-routing.module.ts -
    +
    -
    - crisis-list.component.ts + page-not-found.component.css + +
    + +
    + + page-not-found.component.html + +
    + +
    + + page-not-found.component.ts + +
    +
    +
    + animations.ts +
    + +
    + app.component.css +
    + +
    + app.component.html +
    + +
    + app.component.ts +
    + +
    + app.module.ts +
    + +
    + app-routing.module.ts +
    +
    main.ts
    +
    + message.service.ts +
    +
    index.html
    @@ -2346,6 +2445,14 @@ Here are the relevant files for this version of the sample application. + + + + + + + + @@ -2354,15 +2461,27 @@ Here are the relevant files for this version of the sample application. - + - + + + + + + + + + - + + + + + @@ -2378,6 +2497,10 @@ Here are the relevant files for this version of the sample application. + + + + @@ -2392,20 +2515,19 @@ It's time to add real features to the app's current placeholder crisis center. Begin by imitating the heroes feature: -* Delete the placeholder crisis center file. -* Create an `app/crisis-center` folder. -* Copy the files from `app/heroes` into the new crisis center folder. +* Create a `crisis-center` subfolder in the `src/app` folder. +* Copy the files and folders from `app/heroes` into the new `crisis-center` folder. * In the new files, change every mention of "hero" to "crisis", and "heroes" to "crises". +* Rename the NgModule files to `crisis-center.module.ts` and `crisis-center-routing.module.ts`. -You'll turn the `CrisisService` into a purveyor of mock crises instead of mock heroes: +You'll use mock crises instead of mock heroes: - + - The resulting crisis center is a foundation for introducing a new concept—**child routing**. You can leave *Heroes* in its current state as a contrast with the *Crisis Center* and decide later if the differences are worthwhile. @@ -2452,9 +2574,15 @@ If your app had many feature areas, the app component trees might look like this ### Child routing component -Add the following `crisis-center.component.ts` to the `crisis-center` folder: +Generate a `CrisisCenter` component in the `crisis-center` folder: - + + ng generate component crisis-center/crisis-center + + +Update the component template to look like this: + + The `CrisisCenterComponent` has the following in common with the `AppComponent`: @@ -2466,11 +2594,7 @@ just as the `AppComponent` is a shell to manage the high-level workflow. Like most shells, the `CrisisCenterComponent` class is very simple, simpler even than `AppComponent`: it has no business logic, and its template has no links, just a title and -`` for the crisis center child views. - -Unlike `AppComponent`, and most other components, it _lacks a selector_. -It doesn't _need_ one since you don't *embed* this component in a parent template, -instead you use the router to *navigate* to it. +`` for the crisis center child component. {@a child-route-config} @@ -2478,14 +2602,19 @@ instead you use the router to *navigate* to it. ### Child route configuration -As a host page for the "Crisis Center" feature, add the following `crisis-center-home.component.ts` to the `crisis-center` folder. +As a host page for the "Crisis Center" feature, generate a `CrisisCenterHome` component in the `crisis-center` folder. - + + ng generate component crisis-center/crisis-center-home -Create a `crisis-center-routing.module.ts` file as you did the `heroes-routing.module.ts` file. -This time, you define **child routes** *within* the parent `crisis-center` route. +Update the template with a welcome message to the `Crisis Center`. + + + +Update the `crisis-center-routing.module.ts` you renamed after copying it from `heroes-routing.module.ts` file. +This time, you define **child routes** *within* the parent `crisis-center` route. @@ -2506,8 +2635,8 @@ of the `CrisisCenterComponent`, not in the `RouterOutlet` of the `AppComponent` The `CrisisListComponent` contains the crisis list and a `RouterOutlet` to display the `Crisis Center Home` and `Crisis Detail` route components. -The `Crisis Detail` route is a child of the `Crisis List`. Since the router [reuses components](#reuse) -by default, the `Crisis Detail` component will be re-used as you select different crises. +The `Crisis Detail` route is a child of the `Crisis List`. The router [reuses components](#reuse) +by default, so the `Crisis Detail` component will be re-used as you select different crises. In contrast, back in the `Hero Detail` route, the component was recreated each time you selected a different hero. At the top level, paths that begin with `/` refer to the root of the application. @@ -2525,7 +2654,7 @@ Apply that logic to navigation within the crisis center for which the parent pat The absolute URL for the latter example, including the `localhost` origin, is - localhost:3000/crisis-center/2 + localhost:4200/crisis-center/2 @@ -2548,12 +2677,17 @@ Here's the complete `crisis-center-routing.module.ts` file with its imports. As with the `HeroesModule`, you must add the `CrisisCenterModule` to the `imports` array of the `AppModule` _before_ the `AppRoutingModule`: + - + - + + + + + Remove the initial crisis center route from the `app-routing.module.ts`. The feature routes are now provided by the `HeroesModule` and the `CrisisCenter` modules. @@ -2640,7 +2774,7 @@ The `ActivatedRoute` is implicit in a `RouterLink` directive. Update the `gotoCrises` method of the `CrisisDetailComponent` to navigate back to the *Crisis Center* list using relative path navigation. - + @@ -2673,7 +2807,7 @@ Multiple outlets can be displaying different content, determined by different ro Add an outlet named "popup" in the `AppComponent`, directly below the unnamed outlet. - + @@ -2696,7 +2830,12 @@ They differ in a few key respects. * They work in combination with other routes. * They are displayed in named outlets. -Create a new component named `ComposeMessageComponent` in `src/app/compose-message.component.ts`. +Generate a new component to compose the message. + + + ng generate component compose-message + + It displays a simple form with a header, an input box for the message, and two buttons, "Send" and "Cancel". @@ -2707,16 +2846,20 @@ and two buttons, "Send" and "Cancel". -Here's the component and its template: +Here's the component, its template and styles: - + - + + + + + @@ -2732,9 +2875,6 @@ Note that the `send()` method simulates latency by waiting a second before "send The `closePopup()` method closes the popup view by navigating to the popup outlet with a `null`. That's a peculiarity covered [below](#clear-secondary-routes). -As with other application components, you add the `ComposeMessageComponent` to the `declarations` of an `NgModule`. -Do so in the `AppModule`. - {@a add-secondary-route} @@ -2756,7 +2896,7 @@ This route now targets the popup outlet and the `ComposeMessageComponent` will d The user needs a way to open the popup. Open the `AppComponent` and add a "Contact" link. - + @@ -2851,7 +2991,7 @@ That's why the popup stays visible as you navigate among the crises and heroes. Clicking the "send" or "cancel" buttons _does_ clear the popup view. To see how, look at the `closePopup()` method again: - + @@ -2948,7 +3088,29 @@ In this next section, you'll extend the crisis center with some new *administrat Those features aren't defined yet. But you can start by adding a new feature module named `AdminModule`. -Create an `admin` folder with a feature module file, a routing configuration file, and supporting components. +Generate an `admin` folder with a feature module file and a routing configuration file. + + + ng generate module admin --routing + + +Next, generate the supporting components. + + + ng generate component admin/admin-dashboard + + + + ng generate component admin/admin + + + + ng generate component admin/manage-crises + + + + ng generate component admin/manage-heroes + The admin feature file structure looks like this: @@ -2962,13 +3124,85 @@ The admin feature file structure looks like this:
    - admin-dashboard.component.ts + admin
    +
    + +
    + admin.component.css +
    + +
    + admin.component.html +
    + +
    + admin.component.ts +
    + +
    +
    - admin.component.ts + admin-dashboard
    +
    + +
    + admin-dashboard.component.css +
    + +
    + admin-dashboard.component.html +
    + +
    + admin-dashboard.component.ts +
    + +
    + +
    + manage-crises +
    + +
    + +
    + manage-crises.component.css +
    + +
    + manage-crises.component.html +
    + +
    + manage-crises.component.ts +
    + +
    + +
    + manage-heroes +
    + +
    + +
    + manage-heroes.component.css +
    + +
    + manage-heroes.component.html +
    + +
    + manage-heroes.component.ts +
    + +
    +
    admin.module.ts
    @@ -2977,14 +3211,6 @@ The admin feature file structure looks like this: admin-routing.module.ts
    -
    - manage-crises.component.ts -
    - -
    - manage-heroes.component.ts -
    -
    @@ -2997,11 +3223,11 @@ feature module, a dashboard route and two unfinished components to manage crises - + - + @@ -3009,11 +3235,11 @@ feature module, a dashboard route and two unfinished components to manage crises - + - + @@ -3025,7 +3251,7 @@ feature module, a dashboard route and two unfinished components to manage crises -Since the admin dashboard `RouterLink` is an empty path route in the `AdminComponent`, it +Although the admin dashboard `RouterLink` is an empty path route in the `AdminComponent`, it is considered a match to any route within the admin feature area. You only want the `Dashboard` link to be active when the user visits that route. Adding an additional binding to the `Dashboard` routerLink, @@ -3036,6 +3262,10 @@ the user navigates to the `/admin` URL and not when navigating to any of the chi
    +{@a component-less-route} + + +##### Component-less route: grouping routes without a component The initial admin routing configuration: @@ -3044,12 +3274,6 @@ The initial admin routing configuration:
    - - -{@a component-less-route} - - -### Component-less route: grouping routes without a component Looking at the child route under the `AdminComponent`, there is a `path` and a `children` property but it's not using a `component`. You haven't made a mistake in the configuration. @@ -3073,7 +3297,7 @@ to register the admin routes. Add an "Admin" link to the `AppComponent` shell so that users can get to this feature. - + @@ -3093,14 +3317,18 @@ Instead you'll write a `canActivate()` guard method to redirect anonymous users login page when they try to enter the admin area. This is a general purpose guard—you can imagine other features -that require authenticated users—so you create an -`auth-guard.service.ts` in the application root folder. +that require authenticated users—so you generate an +`AuthGuard` in the `auth` folder. + + + ng generate guard auth/auth + At the moment you're interested in seeing how guards work so the first version does nothing useful. It simply logs to console and `returns` true immediately, allowing navigation to proceed: - + @@ -3115,7 +3343,7 @@ update the admin route with a `canActivate` guard property that references it: - + The admin feature is now protected by the guard, albeit protected poorly. @@ -3126,11 +3354,15 @@ The admin feature is now protected by the guard, albeit protected poorly. Make the `AuthGuard` at least pretend to authenticate. -The `AuthGuard` should call an application service that can login a user and retain information about the current user. -Here's a demo `AuthService`: +The `AuthGuard` should call an application service that can login a user and retain information about the current user. Generate a new `AuthService` in the `admin` folder: + + ng generate service admin/auth + - +Update the `AuthService` to log in the user: + + @@ -3145,7 +3377,7 @@ The `redirectUrl` property will store the attempted URL so you can navigate to i Revise the `AuthGuard` to call it. - + @@ -3175,42 +3407,34 @@ You need a `LoginComponent` for the user to log in to the app. After logging in, to the stored URL if available, or use the default URL. There is nothing new about this component or the way you wire it into the router configuration. -Register a `/login` route in the `login-routing.module.ts` and add the necessary providers to the `providers` -array. In `app.module.ts`, import the `LoginComponent` and add it to the `AppModule` `declarations`. -Import and add the `LoginRoutingModule` to the `AppModule` imports as well. + + ng generate component auth/login + + +Register a `/login` route in the `auth/auth-routing.module.ts`. In `app.module.ts`, import and add the `AuthModule` to the `AppModule` imports. - + - + + + + + - + - -
    - - - -Guards and the service providers they require _must_ be provided at the module-level. This allows -the Router access to retrieve these services from the `Injector` during the navigation process. -The same rule applies for feature modules loaded [asynchronously](#asynchronous-routing). - - -
    - - - {@a can-activate-child-guard} @@ -3224,7 +3448,7 @@ You protected the admin feature module from unauthorized access. You should also protect child routes _within_ the feature module. Extend the `AuthGuard` to protect when navigating between the `admin` routes. -Open `auth-guard.service.ts` and add the `CanActivateChild` interface to the imported tokens from the router package. +Open `auth.guard.ts` and add the `CanActivateChild` interface to the imported tokens from the router package. Next, implement the `canActivateChild()` method which takes the same arguments as the `canActivate()` method: an `ActivatedRouteSnapshot` and `RouterStateSnapshot`. @@ -3233,7 +3457,7 @@ async checks and a `boolean` for sync checks. This one returns a `boolean`: - + @@ -3281,7 +3505,7 @@ You need the `CanDeactivate` guard. {@a cancel-save} -### Cancel and save +#### Cancel and save The sample application doesn't talk to a server. Fortunately, you have another way to demonstrate an asynchronous router hook. @@ -3294,7 +3518,7 @@ discards the changes when the user presses the *Cancel* button. Both buttons navigate back to the crisis list after save or cancel. - + @@ -3322,7 +3546,17 @@ is like waiting for the server asynchronously. -The `DialogService`, provided in the `AppModule` for app-wide use, does the asking. +Generate a `Dialog` service to handle user confirmation. + + + ng generate service dialog + + +Add a `confirm()` method to the `DialogService` to prompt the user to confirm their intent. The `window.confirm` is a _blocking_ action that displays a modal dialog and waits for user interaction. + + + + It returns an `Observable` that *resolves* when the user eventually decides what to do: either to discard changes and navigate away (`true`) or to preserve the pending changes and stay in the crisis editor (`false`). @@ -3331,7 +3565,12 @@ to discard changes and navigate away (`true`) or to preserve the pending changes {@a CanDeactivate} -Create a _guard_ that checks for the presence of a `canDeactivate()` method in a component—any component. +Generate a _guard_ that checks for the presence of a `canDeactivate()` method in a component—any component. + + + ng generate guard can-deactivate + + The `CrisisDetailComponent` will have this method. But the guard doesn't have to know that. The guard shouldn't know the details of any component's deactivation method. @@ -3339,7 +3578,7 @@ It need only detect that the component has a `canDeactivate()` method and call i This approach makes the guard reusable. - + @@ -3354,7 +3593,7 @@ wanted to use this guard for this component and needed to get the component's properties or confirm whether the router should allow navigation away from it. - + @@ -3363,7 +3602,7 @@ the component's properties or confirm whether the router should allow navigation Looking back at the `CrisisDetailComponent`, it implements the confirmation workflow for unsaved changes. - + @@ -3383,17 +3622,6 @@ Add the `Guard` to the crisis detail route in `crisis-center-routing.module.ts` - -Add the `Guard` to the main `AppRoutingModule` `providers` array so the -`Router` can inject it during the navigation process. - - - - - - - - Now you have given the user a safeguard against unsaved changes. {@a Resolve} @@ -3421,7 +3649,7 @@ You need a *resolver*. {@a fetch-before-navigating} -### Fetch data before navigating +#### Fetch data before navigating At the moment, the `CrisisDetailComponent` retrieves the selected crisis. If the crisis is not found, it navigates back to the crisis list view. @@ -3430,17 +3658,21 @@ The experience might be better if all of this were handled first, before the rou A `CrisisDetailResolver` service could retrieve a `Crisis` or navigate away if the `Crisis` does not exist _before_ activating the route and creating the `CrisisDetailComponent`. -Create the `crisis-detail-resolver.service.ts` file within the `Crisis Center` feature area. +Generate a `CrisisDetailResolver` service file within the `Crisis Center` feature area. + + + ng generate service crisis-center/crisis-detail-resolver + - + Take the relevant parts of the crisis retrieval logic in `CrisisDetailComponent.ngOnInit` -and move them into the `CrisisDetailResolver`. +and move them into the `CrisisDetailResolverService`. Import the `Crisis` model, `CrisisService`, and the `Router` so you can navigate elsewhere if you can't fetch the crisis. @@ -3452,17 +3684,20 @@ That method could return a `Promise`, an `Observable`, or a synchronous return v The `CrisisService.getCrisis` method returns an observable, in order to prevent the route from loading until the data is fetched. The `Router` guards require an observable to `complete`, meaning it has emitted all of its values. You use the `take` operator with an argument of `1` to ensure that the -observable completes after retrieving the first value from the observable returned by the -`getCrisis` method. If it doesn't return a valid `Crisis`, navigate the user back to the `CrisisListComponent`, -canceling the previous in-flight navigation to the `CrisisDetailComponent`. +Observable completes after retrieving the first value from the Observable returned by the +`getCrisis` method. + +If it doesn't return a valid `Crisis`, return an empty `Observable`, canceling the previous in-flight navigation to the `CrisisDetailComponent` and navigate the user back to the `CrisisListComponent`. The update resolver service looks like this: + + + + Import this resolver in the `crisis-center-routing.module.ts` and add a `resolve` object to the `CrisisDetailComponent` route configuration. -Remember to add the `CrisisDetailResolver` service to the `CrisisCenterRoutingModule`'s `providers` array. - - + @@ -3474,7 +3709,7 @@ that's where you said it should be when you re-configured the route. It will be there when the `CrisisDetailComponent` ask for it. - + @@ -3483,7 +3718,7 @@ It will be there when the `CrisisDetailComponent` ask for it. **Three critical points** 1. The router's `Resolve` interface is optional. -The `CrisisDetailResolver` doesn't inherit from a base class. +The `CrisisDetailResolverService` doesn't inherit from a base class. The router looks for that method and calls it if found. 1. Rely on the router to call the resolver. @@ -3498,15 +3733,15 @@ The relevant *Crisis Center* code for this milestone follows. - + - + - + @@ -3514,11 +3749,19 @@ The relevant *Crisis Center* code for this milestone follows. - + - + + + + + + + + + @@ -3530,17 +3773,21 @@ The relevant *Crisis Center* code for this milestone follows. - - - - - - - + - + + +Guards + + + + + + + + @@ -3570,7 +3817,7 @@ Add an `anchor` element so you can jump to a certain point on the page. Add the `NavigationExtras` object to the `router.navigate` method that navigates you to the `/login` route. - + @@ -3583,7 +3830,7 @@ and provide the `queryParamsHandling` and `preserveFragment` to pass along the c and fragment to the next route. - + @@ -3598,11 +3845,11 @@ when navigating. -Since you'll be navigating to the *Admin Dashboard* route after logging in, you'll update it to handle the +As you'll be navigating to the *Admin Dashboard* route after logging in, you'll update it to handle the query parameters and fragment. - + @@ -3671,16 +3918,20 @@ Users will still visit `/admin` and the `AdminComponent` still serves as the *Ro Open the `AppRoutingModule` and add a new `admin` route to its `appRoutes` array. -Give it a `loadChildren` property (not a `children` property!), set to the address of the `AdminModule`. +Give it a `loadChildren` property instead of a `children` property, set to the address of the `AdminModule`. The address is the `AdminModule` file location (relative to the app root), -followed by a `#` separator, -followed by the name of the exported module class, `AdminModule`. +followed by a `#` separator, followed by the name of the exported module class, `AdminModule`. +
    + +*Note*: When using absolute paths, the `NgModule` file location must begin with `src/app` in order to resolve correctly. For custom [path mapping with absolute paths](https://www.typescriptlang.org/docs/handbook/module-resolution.html#path-mapping), the `baseUrl` and `paths` properties in the project `tsconfig.json` must be configured. + +
    When the router navigates to this route, it uses the `loadChildren` string to dynamically load the `AdminModule`. @@ -3727,13 +3978,13 @@ Add a **`CanLoad`** guard that only loads the `AdminModule` once the user is log The existing `AuthGuard` already has the essential logic in its `checkLogin()` method to support the `CanLoad` guard. -Open `auth-guard.service.ts`. +Open `auth.guard.ts`. Import the `CanLoad` interface from `@angular/router`. Add it to the `AuthGuard` class's `implements` list. Then implement `canLoad()` as follows: - + @@ -3818,12 +4069,13 @@ Take the same steps you used to configure `AdminModule` for lazy load. 1. Remove all mention of the `CrisisCenterModule` from `app.module.ts`. + Here are the updated modules _before enabling preload_: - + @@ -3855,7 +4107,7 @@ Add the `PreloadAllModules` token to the `forRoot` call: This tells the `Router` preloader to immediately load _all_ lazy loaded routes (routes with a `loadChildren` property). -When you visit `http://localhost:3000`, the `/heroes` route loads immediately upon launch +When you visit `http://localhost:4200`, the `/heroes` route loads immediately upon launch and the router starts loading the `CrisisCenterModule` right after the `HeroesModule` loads. Surprisingly, the `AdminModule` does _not_ preload. Something is blocking it. @@ -3898,18 +4150,20 @@ Set the `data.preload` flag in the `crisis-center` route in the `AppRoutingModul +Generate a new `SelectivePreloadingStrategy` service. + + + ng generate service selective-preloading-strategy + -Add a new file to the project called `selective-preloading-strategy.ts` -and define a `SelectivePreloadingStrategy` service class as follows: - - + -`SelectivePreloadingStrategy` implements the `PreloadingStrategy`, which has one method, `preload`. +`SelectivePreloadingStrategyService` implements the `PreloadingStrategy`, which has one method, `preload`. The router calls the `preload` method with two arguments: @@ -3923,27 +4177,27 @@ If the route should _not_ preload, it returns an `Observable` of `null`. In this sample, the `preload` method loads the route if the route's `data.preload` flag is truthy. It also has a side-effect. -`SelectivePreloadingStrategy` logs the `path` of a selected route in its public `preloadedModules` array. +`SelectivePreloadingStrategyService` logs the `path` of a selected route in its public `preloadedModules` array. Shortly, you'll extend the `AdminDashboardComponent` to inject this service and display its `preloadedModules` array. But first, make a few changes to the `AppRoutingModule`. -1. Import `SelectivePreloadingStrategy` into `AppRoutingModule`. -1. Replace the `PreloadAllModules` strategy in the call to `forRoot` with this `SelectivePreloadingStrategy`. -1. Add the `SelectivePreloadingStrategy` strategy to the `AppRoutingModule` providers array so it can be injected +1. Import `SelectivePreloadingStrategyService` into `AppRoutingModule`. +1. Replace the `PreloadAllModules` strategy in the call to `forRoot` with this `SelectivePreloadingStrategyService`. +1. Add the `SelectivePreloadingStrategyService` strategy to the `AppRoutingModule` providers array so it can be injected elsewhere in the app. Now edit the `AdminDashboardComponent` to display the log of preloaded routes. -1. Import the `SelectivePreloadingStrategy` (it's a service). +1. Import the `SelectivePreloadingStrategyService`. 1. Inject it into the dashboard's constructor. 1. Update the template to display the strategy service's `preloadedModules` array. When you're done it looks like this. - + @@ -3991,9 +4245,15 @@ So instead, you'll update the empty path route in `app-routing.module.ts` to red -Since `RouterLink`s aren't tied to route configuration, you'll need to update the associated router links so they remain active when the new route is active. You'll update the `app.component.ts` template for the `/heroes` routerLink. +`RouterLink`s aren't tied to route configuration, so you'll need to update the associated router links so they remain active when the new route is active. You'll update the `app.component.ts` template for the `/heroes` routerLink. - + + + + +Update the `goToHeroes()` method in the `hero-detail.component.ts` to navigate back to `/superheroes` with the optional route parameters. + + @@ -4016,7 +4276,7 @@ examining its `config` property. For example, update the `AppModule` as follows and look in the browser console window to see the finished route configuration. - + @@ -4066,7 +4326,7 @@ You can bind the `RouterLink` directive to such an array like this: You've written a two element array when specifying a route parameter like this: - + @@ -4220,6 +4480,47 @@ without hashes (#) in the middle. Stick with the default unless you have a compelling reason to resort to hash routes. + +#### The *<base href>* + +The router uses the browser's +history.pushState +for navigation. Thanks to `pushState`, you can make in-app URL paths look the way you want them to +look, e.g. `localhost:4200/crisis-center`. The in-app URLs can be indistinguishable from server URLs. + +Modern HTML5 browsers were the first to support `pushState` which is why many people refer to these URLs as +"HTML5 style" URLs. + + +
    + + + +HTML5 style navigation is the router default. +In the [LocationStrategy and browser URL styles](#browser-url-styles) Appendix, +learn why HTML5 style is preferred, how to adjust its behavior, and how to switch to the +older hash (#) style, if necessary. + + +
    + + + +You must **add a +<base href> element** +to the app's `index.html` for `pushState` routing to work. +The browser uses the `` value to prefix *relative* URLs when referencing +CSS files, scripts, and images. + +Add the `` element just after the `` tag. +If the `app` folder is the application root, as it is for this application, +set the `href` value in **`index.html`** *exactly* as shown here. + + + + + + #### HTML5 URLs and the *<base href>* While the router uses the