AppComponent正在做所有的事情。 一开始,它展示了一个英雄的细节。 然后,它成为一个英雄和英雄细节列表的主/细节形式。 很快就会有新的要求和能力。 您不能在一个组件之上填充所有功能; 这是不可维护的。
您需要将其分解为子组件,每个子组件都专注于特定的任务或工作流程。 最终,AppComponent可以成为托管这些子组件的简单shell。
在这个页面中,您将通过将英雄细节划分为单独的,可重用的组件来迈向这个方向的第一步。 当你完成后,应用程序应该看起来像这样。
从哪里开始
在开始使用本页面之前,请确认您之前在“Tour of Heroes”中拥有以下结构。 如果没有,请返回前面的页面。
如果还没运行,请启动应用,保持应用处于运行状态
创建英雄详情组件
创建文件:hero_detail_component.dart,这个文件将控制新组件HeroDetailComponent
Angular 约定
- 组件类名称使用驼峰命名法并以“Component”结尾,例:HeroDetailComponent
- 组件文件的名称使用Snake Case命名法-单词小写且使用下划线分割以_component结束,例:hero_detail_component.dart
- 内部实现文件应该放在lib / src下。 有关详细信息,请参阅pub package布局约定。
编写HeroDetailComponent文件:lib/src/hero_detail_component.dart (initial version)
import 'package:angular/angular.dart';
import 'package:angular_forms/angular_forms.dart';
@Component(
selector: 'hero-detail',
directives: const [CORE_DIRECTIVES, formDirectives],
)
class HeroDetailComponent {
}
创建一个组件,一定要引入主Angular 库。
@Component注解提供组件的Angular元数据。 CSS选择器名称hero-detail将与在父组件的模板中标识该组件的元素标签相匹配。 在本教程页面结尾处,您将向AppComponent模板添加一个<hero-detail>元素。
Hero详情模板
要将英雄细节视图移动到HeroDetailComponent,请从AppComponent模板的底部切割英雄细节内容,并将其粘贴到@Component注解的新模板参数中。
HeroDetailComponent有一个英雄,而不是一个选定的英雄。 在模板中的任何地方用单词“hero”替换“selectedHero”。 完成后,新模板应该如下所示:lib/src/hero_detail_component.dart (template)
template: '''
<div *ngIf="hero != null">
<h2>{{hero.name}} details!</h2>
<div><label>id: </label>{{hero.id}}</div>
<div>
<label>name: </label>
<input [(ngModel)]="hero.name" placeholder="name">
</div>
</div>''',
添加英雄属性
HeroDetailComponent模板绑定到组件的英雄属性。 将该属性与必要的导入一起添加到HeroDetailComponent类中。lib/src/hero_detail_component.dart (hero)
import 'hero.dart';
class HeroDetailComponent {
Hero hero;
}
英雄属性是一个输入属性
在此页面的后面,父AppComponent将通过将其selectedHero绑定到HeroDetailComponent的hero属性来通知子HeroDetailComponent显示哪个英雄。 绑定将如下所示:
<hero-detail [hero]="selectedHero"></hero-detail>
在等号(=)左侧的英雄属性周围放置方括号使其成为属性绑定表达式的目标。 您必须将目标绑定属性声明为输入属性。 否则,Angular拒绝绑定并抛出一个错误。
声明这个hero是一个输入属性,用@Input()注释它:lib/src/hero_detail_component.dart (Input annotation)
@Input()
Hero hero;
在属性指令页面中了解有关输入属性的更多信息。
hero属性是HeroDetailComponent类中唯一的东西。 它所做的就是通过其hero输入属性接收一个hero对象,然后绑定该属性到模板上。 这是完整的HeroDetailComponent。
lib/src/hero_detail_component.dart
import 'package:angular/angular.dart';
import 'package:angular_forms/angular_forms.dart';
import 'hero.dart';
@Component(
selector: 'hero-detail',
template: '''
<div *ngIf="hero != null">
<h2>{{hero.name}} details!</h2>
<div><label>id: </label>{{hero.id}}</div>
<div>
<label>name: </label>
<input [(ngModel)]="hero.name" placeholder="name">
</div>
</div>''',
directives: const [CORE_DIRECTIVES, formDirectives],
)
class HeroDetailComponent {
@Input()
Hero hero;
}
将HeroDetailComponent添加到AppComponent
AppComponent仍然是主/明细视图。 它用于在切出模板部分之前显示英雄细节。 现在它将委托给HeroDetailComponent。
首先导入HeroDetailComponent,以便AppComponent可以引用它。
import 'src/hero_detail_component.dart';
回想一下,hero-detail是HeroDetailComponent元数据中的CSS选择器。 这是代表HeroDetailComponent的元素的标签名称。
在AppComponent模板的底部附近添加一个<hero-detail>元素,英雄细节视图。
通过将AppComponent的selectedHero属性绑定到HeroDetailComponent的hero属性来将主AppComponent与HeroDetailComponent进行协调。
<hero-detail [hero]="selectedHero"></hero-detail>
现在每当selectedHero改变时,HeroDetailComponent就会得到一个新的hero显示。
修改后的AppComponent模板应该如下所示:lib/app_component.html
<h1>{{title}}</h1>
<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>
<hero-detail [hero]="selectedHero"></hero-detail>
每次用户选择一个新的英雄时,细节应该更新。 这还没有发生! 点击一个英雄。 没有细节。 如果您在浏览器开发工具的控制台中查找错误。 没有错误。
就好像Angular忽略了新的标签。 那是因为它忽略了新的标签。
指令列表
浏览器会忽略不能识别的HTML标签和属性。 Angular也是如此。
你已经导入了HeroDetailComponent,并且你已经在模板中使用了<hero-detail>,但是你还没有将它告诉给Angular。
就像您为内建的Angular指令所做的那样,通过将其列在元数据指令列表中,告诉Angular关于英雄详细信息组件。 你不需要formDirectives,所以删除它,并在文件顶部的angular_forms导入:lib/app_component.dart (directives)
directives: const [CORE_DIRECTIVES, HeroDetailComponent],
刷新浏览器。 该应用程序工作!
应用程序设计更改
和以前一样,每当用户点击一个英雄名字时,英雄详情就会出现在英雄列表的下方。 但是现在HeroDetailComponent正在呈现这些细节。
将原始AppComponent重构为两个组件,现在和将来都会带来好处:
- 您通过减少其职责简化了AppComponent。
- 您可以将HeroDetailComponent演变成一个丰富的英雄编辑器,而无需触摸父AppComponent。
- 你可以在不触及英雄详情视图的情况下演化AppComponent。
- 您可以在将来的某个父组件的模板中重用HeroDetailComponent。
查看应用程序结构
确认您具有以下结构:
这里是本页讨论的代码文件
lib/src/hero_detail_component.dart
import 'package:angular/angular.dart';
import 'package:angular_forms/angular_forms.dart';
import 'hero.dart';
@Component(
selector: 'hero-detail',
template: '''
<div *ngIf="hero != null">
<h2>{{hero.name}} details!</h2>
<div><label>id: </label>{{hero.id}}</div>
<div>
<label>name: </label>
<input [(ngModel)]="hero.name" placeholder="name">
</div>
</div>''',
directives: const [CORE_DIRECTIVES, formDirectives],
)
class HeroDetailComponent {
@Input()
Hero hero;
}
lib/app_component.dart
import 'package:angular/angular.dart';
import 'src/hero.dart';
import 'src/hero_detail_component.dart';
import 'src/mock_heroes.dart';
@Component(
selector: 'my-app',
templateUrl: 'app_component.html',
styleUrls: const ['app_component.css'],
directives: const [CORE_DIRECTIVES, HeroDetailComponent],
)
class AppComponent {
final title = 'Tour of Heroes';
List<Hero> heroes = mockHeroes;
Hero selectedHero;
void onSelect(Hero hero) => selectedHero = hero;
}
lib/app_component.html
<h1>{{title}}</h1>
<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>
<hero-detail [hero]="selectedHero"></hero-detail>
你走过的路
以下是您在此页面中所取得的成果:
- 您创建了一个可重用的组件。
- 您学习了如何使组件接受输入。
- 您学会了在 directives列表中声明应用程序指令。
- 您学会了将父组件绑定到子组件。
前方的路
“The Tour of Heroes ”游戏的应用程序可以更多地使用共享组件,但其(模拟)数据仍然是在AppComponent中硬编码的。 这是不可持续的。 数据访问应重构为单独的服务,并在需要数据的组件之间共享。
您将学习在下一个教程页面中创建服务。
来源:oschina
链接:https://my.oschina.net/u/3647851/blog/1568151