Angular13 Angular 英雄之旅-显示列表

2024-02-25 开发教程 Angular13 匿名 4

显示英雄列表

本页中,你将扩展《英雄之旅》应用,让它显示一个英雄列表,并允许用户选择一个英雄,查看该英雄的详细信息。

要查看本页所讲的范例程序,参阅现场演练/ 下载范例

创建模拟(mock)的英雄数据

你需要一些英雄数据以供显示。

最终,你会从远端的数据服务器获取它。不过目前,你要先创建一些模拟的英雄数据,并假装它们是从服务器上取到的。

在 ​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;
}

使用 *ngFor 列出这些英雄

打开 ​HeroesComponent ​的模板文件,并做如下修改:

  1. 在顶部添加 ​<h2>​。
  2. 在它下面添加一个 HTML 无序列表 ( ​<ul>​ ) 元素。
  3. 在 ​<ul>​ 中插入 ​<li>​。
  4. 在 ​<li>​ 中放一个 ​<button>​ 元素,以便在 ​<span>​ 元素中显示单个 ​hero ​的属性。
  5. 点缀上一些 CSS 类(稍后你还会添加更多 CSS 样式)。

做完之后应该是这样的:

<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

来自 HeroesComponent类的存放模拟(mock)英雄的列表。

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。

查看详情

当用户在此列表中点击一个英雄时,该组件应该在页面底部显示所选英雄的详情。

在本节,你将监听英雄条目的点击事件,并显示与更新英雄的详情。

添加 click 事件绑定

为 ​<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​)。

添加 click 事件处理器

把该组件的 ​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​ 的样式。

  • src/app/mock-heroes.ts
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' }
];
src/app/heroes/heroes.component.ts
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;
}
}
src/app/heroes/heroes.component.html
<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>
src/app/heroes/heroes.component.css
/* HeroesComponent's private CSS styles */
.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 的样式类。