路由是导航的另一个名字。路由器就是从一个视图导航到另一个视图的机制。
1.导航,那么我们专门做一个导航组件。这个组件只是导航使用。一般导航的都是第一个页面,而我们现在启动的就是app.component.ts。
但是此页面目前显示的是列表,我们需要修改下,修改成从主页导航到列表页。
那么我们如此做:
(1)把app.component.ts文件改名为heroes.component.ts。
(2)把AppComponent类改名为HeroesComponent(注意,只在这一个文件中改名)。
(3)把app-test选择器改名为'app-my-heroes'。
2.创建导航页面,即新的app.component.ts。
新的AppComponent将成为应用的“壳”。 它将在顶部放一些导航链接,并且把我们要导航到的页面放在下面的显示区中。
执行下列步骤(请严格按照此步骤做,不能跳):
(1)创建一个名叫src/app/app.component.ts的新文件。
(2)添加支持性的import语句。
(3)定义一个导出的 AppComponent类。
(4)在类的上方添加@Component元数据装饰器,装饰器带有app-test选择器。
(5)将下面的项目从HeroesComponent移到AppComponent:
(5.1)title类属性
(5.2)@Component模板中的<h1>标签,它包含了对title属性的绑定。
(5.3)在模板的标题下面添加<app-my-heroes>标签,以便我们仍能看到英雄列表。
(6)添加HeroesComponent组件到根模块的declarations数组中,以便 Angular 能认识<app-my-heroes>标签。组件都需要添加到此处。
(7)添加HeroService到AppModule的providers数组中,因为我们的每一个视图都需要它。
(8)从HerosComponent的providers数组中移除HeroService,因为它被提到模块了。
(9)为AppComponent添加一些import语句。
实际文件:app.component.ts文件
import { Component } from '@angular/core';
@Component({
selector: 'app-test',
template: `
<h1>{{title}}</h1>
<my-heroes></my-heroes>//这个就是列表页面
`
})
export class AppComponent {
title = 'Tour of Heroes';
}
app.module.ts文件:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { HeroDetailComponent } from './hero-detail.component';
import { HeroesComponent } from './heroes.component';//包含
import { HeroService } from './hero.service';//包含
@NgModule({
imports: [
BrowserModule,
FormsModule
],
declarations: [
AppComponent,
HeroDetailComponent,
HeroesComponent//添加这个
],
providers: [//移到此
HeroService
],
bootstrap: [ AppComponent ]
})
export class AppModule {
}
上面2步其实没有做实际的东西,就是拆成2个组件而已,没有做实际意义的路由的处理。
3.现在开始处理路由:
(1)打开index.html,确保它的<head>区顶部有一个<base href="/">元素
(2)配置路由。app.module.ts中
(2.1)import { RouterModule } from '@angular/router';
(2.2)
imports: [
BrowserModule,
FormsModule, // <-- import the FormsModule before binding with [(ngModel)]
RouterModule.forRoot([//放到这里
{
path: 'heroes',
component: HeroesComponent
}
])
],
(3)修改app.component.ts的模板:
template: <h1>{{title}}</h1> <a routerLink="/heroes">Heroes</a> <router-outlet></router-outlet>//显示路由到的组件的内容
这样就可以实现路由了。
注意:这个时候模板中的routerLink的属性值和app.module.ts中的path的关系可以看明白了。这就是路由切换了,比较简单的。
路由定义包括以下部分:
(1)Path: 路由器会用它来匹配浏览器地址栏中的地址,如heroes。
(2)Component: 导航到此路由时,路由器需要创建的组件(HeroesComponent)。
那个forroot是什么意思?下回分解。
4.添加路由,其实很简单,不信,再添加一个试试。
(1)添加一个新文件dashboard.component.ts然后内容写上:
import { Component } from '@angular/core';
@Component({
selector: 'app-my-dashboard',
template: '<h3>My Dashboard</h3>'
})
export class DashboardComponent { }
(2)配置新的路由:
RouterModule.forRoot([
{
path: 'heroes',
component: HeroesComponent
},
{
path: 'dashboard',//添加一个这个而已
component: DashboardComponent
}
])
(3)将新的组件添加到module,这个很简单,大家都应该知道如何添加了。先import { HeroesComponent } from './heroes.component';包含它,然后
declarations: [
HeroDetailComponent,
HeroesComponent,
AppComponent,
DashboardComponent//放到这里
],
这样就包含了。
(4)这样就算是准备好了。再来,这个是使用:
改变app.component.ts的模板:
template: `
<h1>{{title}}</h1>
<a routerLink="/heroes">Heroes</a>
<router-outlet></router-outlet>
<a routerLink="/dashboard">Dashboard</a>
<router-outlet></router-outlet>
`
这样就是两个页面切换了,很简单吧。
注:上面的写法有点啰嗦,可以如此写:
template: `
<h1>{{title}}</h1>
<a routerLink="/heroes">Heroes</a>
<a routerLink="/dashboard">Dashboard</a>
<router-outlet></router-outlet>
`
5.添加重定向,让启动就显示一个组件:
RouterModule.forRoot([
{
path: 'heroes',
component: HeroesComponent
},
{
path: 'dashboard',
component: DashboardComponent
},
{
path: '',//路径是根
redirectTo: '/dashboard',//重定向了
pathMatch: 'full'
}
])
也是很简单的。
6.美化下dashboard.component这个组件:
(1)dashboard.component.ts文件修改为:
import { Component, OnInit } from '@angular/core';
import { Hero } from './hero';
import { HeroService } from './hero.service';//包含service
@Component({
selector: 'app-my-dashboard',
templateUrl: './dashboard.component.html',//使用文件显示
})
export class DashboardComponent implements OnInit {
heroes: Hero[] = [];
constructor(private heroService: HeroService) { }//构造方法申明私有变量
ngOnInit(): void {//初始化,使用承诺接口
this.heroService.getHeroes()
.then(heroes => this.heroes = heroes.slice(1, 5));
}
}
(2)添加dashboard.component.html文件,内容如下:
<h3>Top Heroes</h3>
<div class="grid grid-pad">
<div *ngFor="let hero of heroes" class="col-1-4">
<div class="module hero">
<h4>{{hero.name}}</h4>
</div>
</div>
</div>
美化完成了。数据使用的服务的数据。
7.路由到详情,并传递数据。前面到详情页面,是使用的<app-hero-detail [hero]="selectedHero"></app-hero-detail>这种方式来传递对象的。现在,我们使用路由的方式导航到详情页面,现在开始改造:
(1)在app.module.ts中做一个参数化的路由:
{
path: 'detail/:id',//路径中的冒号 (:) 表示:id是一个占位符,当导航到这个HeroDetailComponent组件时,它将被填入一个特定英雄的id。
component: HeroDetailComponent
}
(2)改造hero-detail.component.ts为:
// Keep the Input import for now, you'll remove it later:
import { Component, Input, OnInit } from '@angular/core';
import { ActivatedRoute, ParamMap } from '@angular/router';
import { Location } from '@angular/common';
import { HeroService } from './hero.service';
import 'rxjs/add/operator/switchMap';
import { Hero } from './hero';
@Component({
selector: 'app-hero-detail',
templateUrl: './hero-detail.component.html',//模板变化
})
export class HeroDetailComponent implements OnInit {
constructor(
private heroService: HeroService,
private route: ActivatedRoute,
private location: Location
) {}
@Input() hero: Hero;
ngOnInit(): void {
this.route.paramMap
.switchMap((params: ParamMap) => this.heroService.getHero(+params.get('id')))
.subscribe(hero => this.hero = hero);
}
goBack(): void {
this.location.back();//利用浏览器的历史堆栈,导航到上一步
}
}
switchMap运算符如何将可观察的路由参数中的 id 映射到一个新的Observable, 即HeroService.getHero()方法的结果。
如果用户在 getHero 请求执行的过程中再次导航这个组件,switchMap 再次调用HeroService.getHero()之前, 会取消之前的请求。
英雄的id是数字,而路由参数的值总是字符串。 所以我们需要通过 JavaScript 的 (+) 操作符把路由参数的值转成数字。
(3)在hero.service.ts中添加:
···
getHero(id: number): Promise<Hero> {
return this.getHeroes()
.then(heroes => heroes.find(hero => hero.id === id));
}//根据ID获取到信息
···
(4)添加hero-detail.component.html文件:
···
<div *ngIf="hero">
<h2>{{hero.name}} details!</h2>
<div>
<label>id: </label>{{hero.id}}</div>
<div>
<label>name: </label>
<input [(ngModel)]="hero.name" placeholder="name" />
</div>
<button (click)="goBack()">Back</button>
</div>
···
(5)点击时,需要切到detail页面,需要改造dashboard.component.html,能够点击和传递参数:
<h3>Top Heroes</h3>
<div class="grid grid-pad">
<a *ngFor="let hero of heroes" [routerLink]="['/detail', hero.id]" class="col-1-4">//这里就是能够点击,并且传递了参数
<div class="module hero">
<h4>{{hero.name}}</h4>
</div>
</a>
</div>
现在就可以用路由的方式导航到detail页面了。还能传递参数呢。
8.上面虽然可以使用了,但是路由的代码太多,让app.module.ts会越变越大,因此,需要将路由提取到一个单独的文件。
(1)做一个单独的文件:app-routing.module.ts并写代码为:
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { DashboardComponent } from './dashboard.component';
import { HeroesComponent } from './heroes.component';
import { HeroDetailComponent } from './hero-detail.component';
const routes: Routes = [
{ path: '', redirectTo: '/dashboard', pathMatch: 'full' },
{ path: 'dashboard', component: DashboardComponent },
{ path: 'detail/:id', component: HeroDetailComponent },
{ path: 'heroes', component: HeroesComponent }
];//以后添加路由添加到此即可。
@NgModule({
imports: [ RouterModule.forRoot(routes) ],
exports: [ RouterModule ]
})
export class AppRoutingModule {}
(2)修改app.module.ts,以便包含这个路由:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms'; // <-- NgModel lives here
import { AppComponent } from './app.component';
import { HeroDetailComponent } from './hero-detail.component';
import { HeroesComponent } from './heroes.component';
import { DashboardComponent } from './dashboard.component';
import { HeroService } from './hero.service';
import { AppRoutingModule } from './app-routing.module';//1、包含它
@NgModule({
imports: [
BrowserModule,
FormsModule, // <-- import the FormsModule before binding with [(ngModel)]
AppRoutingModule//2.放到此,添加模块
],
declarations: [
HeroDetailComponent,
HeroesComponent,
AppComponent,
DashboardComponent,
],
bootstrap: [ AppComponent ],
providers: [HeroService]
})
export class AppModule { }
通过上面2步,就将路由单独列出了。
9.也可以通过代码跳转到详细页面,当然也是路由的方式。
(1).将heroes.component.ts中模板中的:
<app-hero-detail [hero]="selectedHero"></app-hero-detail>
替换成一个按钮:
<div *ngIf="selectedHero">
<h2>
{{selectedHero.name | uppercase}} is my hero
</h2>
<button (click)="gotoDetail()">View Details</button>//这个按钮
</div>
(2)在类中:
import { Router } from '@angular/router';//1.包含
constructor(
private router: Router,
private heroService: HeroService) { }//2.构造函数中申明
gotoDetail(): void {
this.router.navigate(['/detail', this.selectedHero.id]);//3.使用代码跳转
}