此刻,HeroesComponent同时显示了英雄列表和所选英雄的详情。
把所有特性都放在同一个组件中,将会使应用“长大”后变得不可维护。 你要把大型组件拆分成小一点的子组件,每个子组件都要集中精力处理某个特定的任务或工作流。
本页面中,你将迈出第一步 —— 把英雄详情移入一个独立的、可复用的HeroDetailComponent。
HeroesComponent将仅仅用来表示英雄列表。HeroDetailComponent将用来表示所选英雄的详情。
你可以访问下面的链接https://github.com/cwiki-us-angular/cwiki-us-angular-tour-of-heroes-master-detail从 GitHub 上查看我们提供源代码。
制作HeroDetailComponent
使用 Angular CLI 生成一个名叫hero-detail的新组件。
ng generate component hero-detail
这个命令会做这些事:
创建一个目录src/app/hero-detail.
在这个目录中会生成四个文件:
作为组件样式的 CSS 文件。
作为组件模板的 HTML 文件。
存放组件类HeroDetailComponent的 TypeScript 文件。
HeroDetailComponent类的测试文件。
该命令还会把HeroDetailComponent添加到src/app/app.module.ts文件中@NgModule的declarations列表中。
编写模板
从HeroesComponent模板的底部把表示英雄详情的 HTML 代码剪切粘贴到所生成的HeroDetailComponent模板中。
所粘贴的 HTML 引用了selectedHero。 新的HeroDetailComponent可以展示任意英雄,而不仅仅所选的。因此还要把模板中的所有selectedHero替换为hero。
完工之后,HeroDetailComponent的模板应该是这样的:
src/app/hero-detail/hero-detail.component.html
<div *ngIf="hero">
<h2>{{hero.name | uppercase}} Details</h2>
<div><span>id: </span>{{hero.id}}</div>
<div>
<label>name:
<input [(ngModel)]="hero.name"placeholder="name"/>
</label>
</div>
</div>
添加@Input()hero 属性
HeroDetailComponent模板中绑定了组件中的hero属性,它的类型是Hero。
打开HeroDetailComponent类文件,并导入Hero符号。
src/app/hero-detail/hero-detail.component.ts (import Hero)
import{ Hero } from '../hero';
hero属性必须是一个带有@Input()装饰器的输入属性,因为外部的HeroesComponent组件将会绑定到它。就像这样:
<app-hero-detail [hero]="selectedHero"></app-hero-detail>
修改@angular/core的导入语句,导入Input符号。
src/app/hero-detail/hero-detail.component.ts (import Input)
import{ Component, OnInit, Input } from '@angular/core';
添加一个带有@Input()装饰器的hero属性。
@Input() hero: Hero;
这就是你要对HeroDetailComponent类做的唯一一项修改。 没有其它属性,也没有展示逻辑。这个组件所做的只是通过hero属性接收一个英雄对象,并显示它。
显示HeroDetailComponent
HeroesComponent仍然是主从视图。
在你从模板中剪切走代码之前,它自己负责显示英雄的详情。现在它要把这个职责委托给HeroDetailComponent了。
这两个组件将会具有父子关系。 当用户从列表中选择了某个英雄时,父组件HeroesComponent将通过把要显示的新英雄发送给子组件HeroDetailComponent,来控制子组件。
你不用修改HeroesComponent类,但是要修改它的模板。
修改HeroesComponent的模板
HeroDetailComponent的选择器是'app-hero-detail'。 把<app-hero-detail>添加到HeroesComponent模板的底部,以便把英雄详情的视图显示到那里。
把HeroesComponent.selectedHero绑定到该元素的hero属性,就像这样:
heroes.component.html (HeroDetail binding)
<app-hero-detail [hero]="selectedHero"></app-hero-detail>
[hero]="selectedHero"是 Angular 的属性绑定语法。
这是一种单向数据绑定。从HeroesComponent的selectedHero属性绑定到目标元素的hero属性,并映射到了HeroDetailComponent的hero属性。
现在,当用户在列表中点击某个英雄时,selectedHero就改变了。 当selectedHero改变时,属性绑定会修改HeroDetailComponent的hero属性,HeroDetailComponent就会显示这个新的英雄。
修改后的HeroesComponent的模板是这样的:
heroes.component.html
<h2>My Heroes</h2>
<ul class="heroes">
<li *ngFor="let hero of heroes"
[class.selected]="hero === selectedHero"
(click)="onSelect(hero)">
<span class="badge">{{hero.id}}</span> {{hero.name}}
</li>
</ul>
<app-hero-detail [hero]="selectedHero"></app-hero-detail>
浏览器刷新,应用又像以前一样开始工作了。
修改了什么?
像以前一样,一旦用户点击了一个英雄的名字,该英雄的详情就显示在了英雄列表下方。 现在,HeroDetailComponent负责显示那些详情,而不再是HeroesComponent。
把原来的HeroesComponent重构成两个组件带来了一些优点,无论是现在还是未来:
你通过缩减HeroesComponent的职责简化了该组件。
你可以把HeroDetailComponent改进成一个功能丰富的英雄编辑器,而不用改动父组件HeroesComponent。
你可以改进HeroesComponent,而不用改动英雄详情视图。
将来你可以在其它组件的模板中重复使用HeroDetailComponent。
查看最终代码
你的应用应该变成了这样在线例子/下载范例。本页所提及的代码文件如下:
如果你想直接在 stackblitz 运行本页中的例子,请单击链接:https://stackblitz.com/github/cwiki-us-angular/cwiki-us-angular-tour-of-heroes-master-detail
本页中所提及的代码如下:https://github.com/cwiki-us-angular/cwiki-us-angular-tour-of-heroes-master-detail
对应的文件列表和代码链接如下:
文件名源代码
src/app/hero-detail/hero-detail.component.tshttps://github.com/cwiki-us-angular/cwiki-us-angular-tour-of-heroes-master-detail/blob/master/src/app/hero-detail/hero-detail.component.ts
src/app/hero-detail/hero-detail.component.htmlhttps://github.com/cwiki-us-angular/cwiki-us-angular-tour-of-heroes-master-detail/blob/master/src/app/hero-detail/hero-detail.component.html
src/app/heroes/heroes.component.htmlhttps://github.com/cwiki-us-angular/cwiki-us-angular-tour-of-heroes-master-detail/blob/master/src/app/heroes/heroes.component.html
src/app/app.module.tshttps://github.com/cwiki-us-angular/cwiki-us-angular-tour-of-heroes-master-detail/blob/master/src/app/app.module.ts
小结
你创建了一个独立的、可复用的HeroDetailComponent组件。
你用属性绑定语法来让父组件HeroesComponent可以控制子组件HeroDetailComponent。
你用@Input装饰器来让hero属性可以在外部的HeroesComponent中绑定。