第七章 模块
点此处可以下载本章完成后的 示例代码 。运行方法为:
- 解压缩文件,进入
heroes
项目根目录。- 使用
npm install
补上依赖包。也可以把之前备份的node_modules
目录复制一份在项目根目录下。- 使用
json-server data.json
启动后端服务。- 使用
npm start
运行项目,打开浏览器访问http://localhost:4200/
进入项目。
到现在为止,我们的主角都一一登场了,组件、路由、服务……已经可以搭建一个简单的典型CRUD应用(指主要对数据库执行Create, Read, Update,Delete操作的应用)了。本章将完善英雄应用,增加新增英雄、删除英雄和查询英雄等功能。由于我们的代码越来越多,在正式操作以前,我们引入一个新的概念:模块,来组织我们的代码文件,使代码更容易维护。
1. 创建模块
Angular应用创建之初,就会自动创建一个模块:根模块,即app.module.ts
。对于小型应用而言,这也够用了。但是,应用稍微大一点以后,组件就需要进行有效的整合管理,将不同的功能组件放在不同的模块中,就显得很必要了。
首先,我们要在src/app/heroes
目录下,创建一个模块文件,名为heroes.module.ts
。这个文件的作用是管理hero相关的组件和服务,所以同时应该把app.module.ts
中hero相关的东西删掉,换上这个模块文件。
因为下面第2步要创建heroes自己的路由模块,所以在heroes.module.ts
中先加上对heroes-routing..module.ts
的引用。
heroes.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { HeroListComponent } from './hero-list/hero-list.component';
import { HeroDetailComponent } from './hero-detail/hero-detail.component';
import { HeroesRoutingModule } from './heroes-routing.module';
import { HeroService } from '../service/hero.service';
@NgModule({
imports: [
CommonModule,
FormsModule,
HeroesRoutingModule
],
declarations: [
HeroListComponent,
HeroDetailComponent
],
providers: [ HeroService ]
})
export class HeroesModule {}
app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { AppComponent } from './app.component';
import { ApiService } from './service/api.service';
import { AppRoutingModule } from './app-routing.module';
import { HeroesModule } from './heroes/heroes.module';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
FormsModule,
HttpModule,
HeroesModule,
AppRoutingModule
],
providers: [
ApiService
],
bootstrap: [AppComponent]
})
export class AppModule { }
2. 创建独立的路由模块
随着英雄指南的功能不断增加,路由也会变得越来越复杂,这时我们就需要把相关的路由信息从app-routing.module.ts
中拿出来,放到一个独立的路由模块中,这样再维护越来就会比较方便。我们仍然选择在src/app/heroes
目录下,新建一个heroes-routing.module.ts
文件。相应的,删掉app-routing.module.ts
中与hero有关的路由。
heroes-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HeroListComponent } from './hero-list/hero-list.component';
import { HeroDetailComponent } from './hero-detail/hero-detail.component';
const routes: Routes = [
{ path: 'heroes', component: HeroListComponent },
{ path: 'hero/:id', component: HeroDetailComponent } //增加英雄详情的路由
];
@NgModule({
imports: [ RouterModule.forChild(routes) ], //注意此处是forChild
exports: [ RouterModule ]
})
export class HeroesRoutingModule {}
app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
const routes: Routes = [
{ path: '', redirectTo: '/heroes', pathMatch: 'full' }
];
@NgModule({
imports: [ RouterModule.forRoot(routes) ],
exports: [ RouterModule ]
})
export class AppRoutingModule {}
3. 修改 hero-list
我们之前是把英雄详情放在英雄列表的下方,这样如果列表很长,就不容易找到了,我们打算用一个单独的页面去展示英雄的详情。
在src/app/heroes/hero-list/hero-list.component.html
的最下方,删除<app-hero-detail [hero]="selectedHero"></app-hero-detail>
这一行。我们以后不在这里显示英雄详情了。
hero-list.component.html
<h2>My Heroes</h2>
<div>
<label>Hero name:</label> <input #heroName />
<button (click)="add(heroName.value); heroName.value=''">
Add
</button>
</div>
<ul class="heroes">
<li *ngFor="let hero of heroes" (click)="onSelect(hero)"
[class.selected]="hero === selectedHero">
<span class="badge">{{hero.id}}</span>
<span>{{hero.name}}</span>
<button class="delete"
(click)="delete(hero); $event.stopPropagation()">x</button>
</li>
</ul>
修改src/app/heroes/hero-list/hero-list.component.ts
,增加add, delete方法,修改onSelect方法。
hero-list.component.ts
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { Hero } from '../../domain/hero';
import { HeroService } from '../../service/hero.service';
@Component({
selector: 'app-hero-list',
templateUrl: './hero-list.component.html',
styleUrls: [ './hero-list.component.css' ]
})
export class HeroListComponent implements OnInit {
heroes: Hero[];
selectedHero: Hero;
constructor(
private heroService: HeroService,
private router: Router) { }
getHeroes(): void {
this.heroService
.getHeroes()
.then(heroes => this.heroes = heroes);
}
add(name: string): void {
name = name.trim();
if (!name) { return; }
this.heroService.create(name)
.then(hero => {
this.heroes.push(hero);
this.selectedHero = null;
});
}
delete(hero: Hero): void {
this.heroService
.delete(hero.id)
.then(() => {
this.heroes = this.heroes.filter(h => h !== hero);
if (this.selectedHero === hero) { this.selectedHero = null; }
});
}
ngOnInit(): void {
this.getHeroes();
}
onSelect(hero: Hero): void {
this.selectedHero = hero;
this.gotoDetail();
}
gotoDetail(): void {
this.router.navigate(['/hero', this.selectedHero.id]);
}
}
相应的样式文件中,增加对button的样式。
hero-list.component.css
button {
font-family: Arial;
background-color: #eee;
border: none;
padding: 5px 10px;
border-radius: 4px;
cursor: pointer;
cursor: hand;
}
button:hover {
background-color: #cfd8dc;
}
button.delete {
float:right;
margin-top: 2px;
margin-right: .8em;
background-color: gray !important;
color:white;
}
4. 修改 hero-detail
在之前的HeroDetailComponent
中,我们是从父组件HeroComponent
以@Input()
的方式获得了要显示的对象。现在我们要修改它获得数据的方式。现在,我们将其改为从路径中获得要显示的英雄的ID。
src/app/heroes/hero-detail/hero-detail.component.ts
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, ParamMap } from '@angular/router';
import { Location } from '@angular/common';
import 'rxjs/add/operator/switchMap';
import { Hero } from '../../domain/hero';
import { HeroService } from '../../service/hero.service';
@Component({
selector: 'app-hero-detail',
templateUrl: './hero-detail.component.html',
styleUrls: [ './hero-detail.component.css' ]
})
export class HeroDetailComponent implements OnInit {
hero: Hero;
constructor(
private heroService: HeroService,
private route: ActivatedRoute,
private location: Location
) {}
ngOnInit(): void {
this.route.paramMap
.switchMap((params: ParamMap) => this.heroService.getHero(+params.get('id')))
.subscribe(hero => this.hero = hero);
}
save(): void {
this.heroService.update(this.hero)
.then(() => this.goBack());
}
goBack(): void {
this.location.back();
}
}