Angular学习6-路由

路由是导航的另一个名字。路由器就是从一个视图导航到另一个视图的机制。

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.使用代码跳转
  }

推荐阅读更多精彩内容

  • Angular 2架构总览 - 简书http://www.jianshu.com/p/aeb11061b82c A...
    葡萄喃喃呓语阅读 1,062评论 2 13
  • 版本:4.0.0+2 有一些英雄指南应用的新需求: 添加一个仪表盘 视图。 添加在英雄 视图和 仪表盘 视图之间导...
    soojade阅读 830评论 0 0
  • Angular 2是一个帮助我们使用HTML和JavaScript构建客户端应用的框架。这个框架包含几个互相协作的...
    JasonQiao阅读 6,398评论 1 48
  • 简要说明:本文主要摘录于 Angular 官网中 JavaScript 的设计风格指南。本风格指南介绍了提倡的约定...
    _仲夏_阅读 727评论 0 2
  • 诗与诗人· 田 秀诗和诗人有什么好谈的,我也不知道我为啥选这个题目.想来想去,还是有谈的,特别实诗人在诗里究竟是什...
    兴安居士阅读 66评论 0 3