本页中,你将扩展《英雄之旅》应用,让它显示一个英雄列表,并允许用户选择一个英雄,查看该英雄的详细信息。
要查看本页所讲的范例程序,参阅现场演练 / 下载范例。
你需要一些英雄数据以供显示。
最终,你会从远端的数据服务器获取它。不过目前,你要先创建一些模拟的英雄数据,并假装它们是从服务器上取到的。
在 src/app/
文件夹中创建一个名叫 mock-heroes.ts
的文件。定义一个包含十个英雄的常量数组 HEROES
,并导出它。该文件是这样的。
import { Hero } from "./hero";
export const HEROES: Hero[] = [
{ id: 12, name: "Dr. Nice" },
{ id: 13, name: "Bombasto" },
{ id: 14, name: "Celeritas" },
{ id: 15, name: "Magneta" },
{ id: 16, name: "RubberMan" },
{ id: 17, name: "Dynama" },
{ id: 18, name: "Dr. IQ" },
{ id: 19, name: "Magma" },
{ id: 20, name: "Tornado" }
];
打开 HeroesComponent
类文件,并导入模拟的 HEROES
。
import { HEROES } from "../mock-heroes";
往类中添加一个 heroes
属性,这样可以暴露出这个 HEROES
数组,以供绑定。
export class HeroesComponent implements OnInit {
heroes = HEROES;
}
打开 HeroesComponent
的模板文件,并做如下修改:
<h2>
。<ul>
) 元素。<ul>
中插入 <li>
。<li>
中放一个 <button>
元素,以便在 <span>
元素中显示单个 hero
的属性。做完之后应该是这样的:
<h2>My Heroes</h2>
<ul class="heroes">
<li>
<button type="button">
<span class="badge">{{hero.id}}</span>
<span class="name">{{hero.name}}</span>
</button>
</li>
</ul>
由于属性 "hero" 不存在,因此会显示一个错误。要访问每个英雄并列出所有英雄,请在 <li>
上添加 *ngFor
以遍历英雄列表:
<li *ngFor="let hero of heroes">
*ngFor
是一个 Angular 的复写器(repeater)指令。它会为列表中的每项数据复写它的宿主元素。
这个例子中涉及的语法如下:
语法 |
详情 |
---|---|
<li>
|
宿主元素。 |
heroes
|
来自 |
hero
|
保存列表每次迭代的当前 hero 对象。 |
不要忘了
ngFor
前面的星号(*
),它是该语法中的关键部分。
浏览器刷新之后,英雄列表出现了。
交互元素
注意:
在 <li>
元素中,我们将英雄的详细信息包装在 <button>
元素中。稍后我们使 hero 可点击,并且出于无障碍性的目的,最好使用本机交互式 HTML 元素(例如 <button>
),而不是向非交互式元素添加事件侦听器(例如 <li>
)。
英雄列表应该富有吸引力,并且当用户把鼠标移到某个英雄上和从列表中选中某个英雄时,应该给出视觉反馈。
在教程的第一章,你曾在 styles.css
中为整个应用设置了一些基础的样式。但那个样式表并不包含英雄列表所需的样式。
固然,你可以把更多样式加入到 styles.css
,并且放任它随着你添加更多组件而不断膨胀。
但还有更好的方式。你可以定义属于特定组件的私有样式,并且让组件所需的一切(代码、HTML 和 CSS)都放在一起。
这种方式让你在其它地方复用该组件更加容易,并且即使全局样式和这里不一样,组件也仍然具有期望的外观。
你可以用多种方式定义私有样式,或者内联在 @Component.styles
数组中,或者在 @Component.styleUrls
所指出的样式表文件中。
当 CLI 生成 HeroesComponent
时,它也同时为 HeroesComponent
创建了空白的 heroes.component.css
样式表文件,并且让 @Component.styleUrls
指向它,就像这样。
@Component({
selector: "app-heroes",
templateUrl: "./heroes.component.html",
styleUrls: ["./heroes.component.css"]
})
打开 heroes.component.css
文件,并且把 HeroesComponent
的私有 CSS 样式粘贴进去。
@Component
元数据中指定的样式和样式表都是局限于该组件的。heroes.component.css
中的样式只会作用于 HeroesComponent
,既不会影响到组件外的 HTML,也不会影响到其它组件中的 HTML。
当用户在此列表中点击一个英雄时,该组件应该在页面底部显示所选英雄的详情。
在本节,你将监听英雄条目的点击事件,并显示与更新英雄的详情。
为 <li>
中的 <button>
上添加一个点击事件的绑定代码:
<li *ngFor="let hero of heroes">
<button type="button" (click)="onSelect(hero)">
<!-- ... -->
click
外面的圆括号会让 Angular 监听这个 <button>
元素的 click
事件。 当用户点击 <button>
时,Angular 就会执行表达式 onSelect(hero)
。
下一部分,会在 HeroesComponent
上定义一个 onSelect()
方法,用来显示 *ngFor
表达式所定义的那个英雄(hero
)。
把该组件的 hero
属性改名为 selectedHero
,但不要为它赋值。 因为应用刚刚启动时并没有所选英雄。
添加如下 onSelect()
方法,它会把模板中被点击的英雄赋值给组件的 selectedHero
属性。
selectedHero?: Hero;
onSelect(hero: Hero): void {
this.selectedHero = hero;
}
现在,组件的模板中有一个列表。要想点击列表中的一个英雄,并显示该英雄的详情,你需要在模板中留一个区域,用来显示这些详情。在 heroes.component.html
中该列表的紧下方,添加如下代码:
<div *ngIf="selectedHero">
<h2>{{selectedHero.name | uppercase}} Details</h2>
<div>id: {{selectedHero.id}}</div>
<div>
<label for="hero-name">Hero name: </label>
<input id="hero-name" [(ngModel)]="selectedHero.name" placeholder="name">
</div>
</div>
只有在选择英雄时才会显示英雄详细信息。最初创建组件时,没有所选的 hero,因此我们将 *ngIf
指令添加到包装 hero 详细信息的 <div>
中,以指示 Angular 仅在实际定义 selectedHero
时(在它被通过点击英雄来选择)。
不要忘了
ngIf
前面的星号(*
),它是该语法中的关键部分。
为了标出选定的英雄,你可以在以前添加过的样式中增加 CSS 类 .selected
。若要把 .selected
类应用于此 <li> 上,请使用类绑定。
Angular 的类绑定可以有条件地添加和删除 CSS 类。只需将 [class.some-css-class]="some-condition"
添加到要设置样式的元素即可。
在 HeroesComponent
模板中的 <button>
元素上添加 [class.selected]
绑定,代码如下:
[class.selected]="hero === selectedHero"
如果当前行的英雄和 selectedHero
相同,Angular 就会添加 CSS 类 selected
,否则就会移除它。
最终的 <li>
是这样的:
<li *ngFor="let hero of heroes">
<button [class.selected]="hero === selectedHero" type="button" (click)="onSelect(hero)">
<span class="badge">{{hero.id}}</span>
<span class="name">{{hero.name}}</span>
</button>
</li>
下面是本页面中所提及的代码文件,包括 HeroesComponent
的样式。
import { Hero } from "./hero";
export const HEROES: Hero[] = [
{ id: 12, name: "Dr. Nice" },
{ id: 13, name: "Bombasto" },
{ id: 14, name: "Celeritas" },
{ id: 15, name: "Magneta" },
{ id: 16, name: "RubberMan" },
{ id: 17, name: "Dynama" },
{ id: 18, name: "Dr. IQ" },
{ id: 19, name: "Magma" },
{ id: 20, name: "Tornado" }
];
import { Component, OnInit } from "@angular/core";
import { Hero } from "../hero";
import { HEROES } from "../mock-heroes";
@Component({
selector: "app-heroes",
templateUrl: "./heroes.component.html",
styleUrls: ["./heroes.component.css"]
})
export class HeroesComponent implements OnInit {
heroes = HEROES;
selectedHero?: Hero;
constructor() { }
ngOnInit(): void {
}
onSelect(hero: Hero): void {
this.selectedHero = hero;
}
}
<h2>My Heroes</h2>
<ul class="heroes">
<li *ngFor="let hero of heroes">
<button [class.selected]="hero === selectedHero" type="button" (click)="onSelect(hero)">
<span class="badge">{{hero.id}}</span>
<span class="name">{{hero.name}}</span>
</button>
</li>
</ul>
<div *ngIf="selectedHero">
<h2>{{selectedHero.name | uppercase}} Details</h2>
<div>id: {{selectedHero.id}}</div>
<div>
<label for="hero-name">Hero name: </label>
<input id="hero-name" [(ngModel)]="selectedHero.name" placeholder="name">
</div>
</div>
.heroes {
margin: 0 0 2em 0;
list-style-type: none;
padding: 0;
width: 15em;
}
.heroes li {
display: flex;
}
.heroes button {
flex: 1;
cursor: pointer;
position: relative;
left: 0;
background-color: #EEE;
margin: .5em;
padding: 0;
border-radius: 4px;
display: flex;
align-items: stretch;
height: 1.8em;
}
.heroes button:hover {
color: #2c3a41;
background-color: #e6e6e6;
left: .1em;
}
.heroes button:active {
background-color: #525252;
color: #fafafa;
}
.heroes button.selected {
background-color: black;
color: white;
}
.heroes button.selected:hover {
background-color: #505050;
color: white;
}
.heroes button.selected:active {
background-color: black;
color: white;
}
.heroes .badge {
display: inline-block;
font-size: small;
color: white;
padding: 0.8em 0.7em 0 0.7em;
background-color: #405061;
line-height: 1em;
margin-right: .8em;
border-radius: 4px 0 0 4px;
}
.heroes .name {
align-self: center;
}
*ngFor
显示了一个列表。
*ngIf
来根据条件包含或排除了一段 HTML。
class
绑定来切换 CSS 的样式类。创建库对于如何创建和发布新库,以扩展Angular的功能,本页面提供了一个概念性的总览如果你发现自己要在多个应用中解决同样的问...
从AngularJS升级到AngularAngular是现在和未来的Angular名称。AngularJS是所有1.x版本的Angular的名称。有很多大型AngularJS应用...
AngularJS Scope(作用域)本节为你介绍了什么是AngularJSScope(作用域)以及 Scope 在 AngularJS 应该如何使用。Scope(作用域) 是...
AngularJS Include(包含) 本节介绍了AngularJSInclude(包含) 的知识,你将学习如何使用ng-include指令。使用 AngularJS, 你可...
XHR和依赖性注入 本文主要为你介绍了 AngularJS XHR 和依赖注入,这里整理了详细资料和示例代码在硬编码的数据集中有三款手机的...
Angular是当下非常流行的前端框架,受到了广大前端开发者的喜爱。下面,将为大家列出一些经典的Angular面试题以及答案,供大家参...
HTMLIndex 文件public/index.html文件是一个会被html-webpack-plugin处理的模板。在构建过程中,资源链接会被自动注入。另外,Vu...
核心概念系统里有两个主要的部分:@vue/cli:全局安装的,暴露vue create app命令;@vue/cli-service:局部安装,暴露vue-cli-se...
自定义指令简介除了默认设置的核心指令(v-model和v-show),Vue 也允许注册自定义指令。注意,在 Vue2.0 里面,代码复用的主要形式...