新新新手阅读 Angular 文件 - Day04

学习目标

这篇内容是纪录阅读官方文件Display a selection list,文章主要内容是介绍如何创一个列表让使用者可以点击其中一个选项,并呈现出该被点击英雄的详细资料。

创出模拟的英雄资料表

这边的范例是延续本系列文章的 Day03 的专案内容。
step 1.
在这边我们会创出一群英雄的静态资料,来模仿我们从服务器接使用者的资料进来。
我们在 src/app 的目录底下创一个档案叫 mock-heroes.ts,里面储存了所有英雄的资料。

--- mock.heroes.ts --
import { HERO } from './hero';

export const HEROES: HERO[] = [
  { id: 11, name: 'Dr Nice' },
  { id: 12, name: 'Narco' },
  { 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 在 hero.ts 档案中定义的 HERO interface 资料格式。
接着,创出一个叫 HEROES 的阵列而且该阵列的元素都依照 HERO 的资料格式来定义该英雄的属性内容。
最後,再将这个创出来的阵列 export 出去,让外部的档案可以引入此档案的内容。

step 2.
我们将这个英雄的资料接到 heroes 元件的 ts 档案中,并将阵列资料指定到 heroes 元件的资料成员,并用 *ngFor 的语法来遍历这个成员,将所有资料渲染到画面中。

--- heroes.component.ts ---
import { HEROES } from '../mock-heroes.ts';

@Component({
  selector: 'app-heroes',
  templateUrl: './heroes.component.html',
  styleUrls: ['./heroes.component.css']
})
export class HeroesComponent implements OnInit {
	// ...
  heroes = HEROES // 将 HEROES 资料指定给 heroes 这个资料成员
}


--- heroes.component.html ---
<ul>
	<li *ngFor="let hero of heroes">
		<span class="badge">{{hero.id}}</span> {{hero.name}}
	</li>
</ul>

完成以上步骤之後,应该可以看到阵列的资料被渲染到画面中罗。

查看英雄的详细资料

这个部分是纪录如何监听物件的点击事件并呈现被点击英雄的详细资料。

---hero.component.html ---
<ul>
	<li *ngFor="let hero of heroes" (click)="onSelect(hero)">
		<span class="badge">{{hero.id}}</span> {{hero.name}}
	</li>
</ul>

可以看到我们在 li 上监听 click 事件且会去呼叫 onSelect 事件并将被点击的英雄物件传入 onSelect 事件。

在 Angular 中,绑定监听事件是用括号(parentheses) 将监听事件包起来,後面跟着要回呼的事件。跟 Vue 的写法蛮像的 @click="onSelect(hero)"

定义点击事件的内容

上面的部分,是写如何在元件的 html 档案上绑定点击的监听事件。
这边就要在 hero.component.ts 中,定义 onSelect 函式内容

--- heroes.component.ts ---
@Component({
  selector: 'app-heroes',
  templateUrl: './heroes.component.html',
  styleUrls: ['./heroes.component.css']
})
export class HeroesComponent implements OnInit {
	// ...
  selectedHero?: Hero  // 限定 selectedHero 的资料型别要跟 Hero 一样
  onSelect(hero:Hero): void{
    this.selectedHero =  hero
  }
}

加入呈现英雄详细资料的画面

这边我们就要将被选择英雄的详细资料渲染到画面上面了。
所以,我们要将 selectedHero 的资料加到 heroes 元件的 html 档案中。

--- heroes.component.html ---
<div>
  <h2>{{selectedHero.name | uppercase}} Details</h2>
  <div><span>id: </span>{{selectedHero.id}}</div>
  <div>
    <label for="hero-name">Hero name: </label>
    <input id="hero-name" [(ngModel)]="selectedHero.name" placeholder="name">
  </div>
</div>

当你加上这些内容之後,重新整理你的网页,应该会报错
https://ithelp.ithome.com.tw/upload/images/20210904/20140093VYwoOMTFmn.png

这个错误提示讯息是说明 selectedHero 这个物件可能是 undefined
会造成这个状况的原因是因为一开始进入网页,我们还没有点击列表中的任何一个英雄,所以,selectedHero 没有被设入任何资料,是一个undefined 的状态,当然就会报错罗。
所以,我们要在外层的 div 中加入 *ngIf 来判定是否 selectedHero 有资料,若有才˙渲染它的内容,以此方式来消除以上的错误。

<div *ngIf="selectedHero">  // 加入 *ngIf 的判定
  <h2>{{selectedHero.name | uppercase}} Details</h2>
  <div><span>id: </span>{{selectedHero.id}}</div>
  <div>
    <label for="hero-name">Hero name: </label>
    <input id="hero-name" [(ngModel)]="selectedHero.name" placeholder="name">
  </div>
</div>

这样就没有错误罗~~

为选择英雄加入指定 CSS 样式

Okay~~ 教完如何在元件上绑定监听事件之後,终於来到之前所提到的为被点击的英雄区块加入特别的 css 样式的部分。
在这个部分,就会讲到要如何动态地加入 className 到 html DOM 上。

Angular 动态加入 className 的语法为: [class.some-css-class]="some-condition"

在方括号中的 class. 後面接的是你想要加入的 className,然後,some-condition 的部分是加入判断式,所以,以上的语法意思为当满足 some-condition 的判断式,就会为此 DOM 加入 some-css-class 的 className。

所以,运用到我们的范例中的话,程序的内容如下

<li *ngFor="let hero of heroes" [class.selected]="hero === selectedHero" (click)="onSelect(hero)">
	<span class="badge">{{hero.id}}</span> {{hero.name}}
</li>

其语法意思为当 hero 和 selectedHero 相同的话,就为这个 li 加上 selected 的 className。
如此就可以达到我们想要的效果罗。

而有关 selected 的详细的 css 样式设定,在官方文件中就有写了,我这边就先不赘述了。

经过以上的操作之後,画面上点选指定英雄之後,可以出现该英雄的详细资料,并且可以透过 input 栏位来更改该英雄的名称,像下面的 gif 呈现的结果
专案最终结果

Summary

这边做的总结,在本章中学到了

  1. 如何为 DOM 绑定监听事件
  2. 如何在元件中定义监听事件的回呼函式
  3. 如何使用 *ngIf 来防止当一进入网页时,某些物件还未被设入资料所造成的错误
  4. 如何利用 [class.some-css-class]="some-condition" 来为 DOM 动态加入 className

<<:  [Day3] Jetpack Compose: 为什麽这个EditText不会动?

>>:  Day 4 ( 入门 ) 一直向下的箭头

Day 18 - for in & for of loop

for in 可以用在 object,也可以用在 Array 使用 for in 列举一个 obje...

恐怖的全端工程师

这个标题有点重复,因为说白了「全端」工程师就是「懂很多」的工程师。 这类工程师受到市场青睐,说白了就...

[Day27] HTB Legacy

URL : https://app.hackthebox.eu/machines/2 IP : 1...

[Day25] Vue3 E2E Testing: Cypress 实战之 Todo MVC (上)

在经过前两天简单的介绍 Cypress,现在我想透过一个实际的范例来让大家更加了解 Cypress ...

要怎麽计算「顾客终身价值(LTV)」?

什麽是LTV Lifetime value 的头字母简称。中文翻为「顾客终身价值」。 意思是,平均而...