Angular 英雄之旅-顯示列表

2022-07-19 09:38 更新

顯示英雄列表

本頁中,你將擴(kuò)展《英雄之旅》應(yīng)用,讓它顯示一個(gè)英雄列表,并允許用戶選擇一個(gè)英雄,查看該英雄的詳細(xì)信息。

要查看本頁所講的范例程序,參閱現(xiàn)場(chǎng)演練 / 下載范例。

創(chuàng)建模擬(mock)的英雄數(shù)據(jù)

你需要一些英雄數(shù)據(jù)以供顯示。

最終,你會(huì)從遠(yuǎn)端的數(shù)據(jù)服務(wù)器獲取它。不過目前,你要先創(chuàng)建一些模擬的英雄數(shù)據(jù),并假裝它們是從服務(wù)器上取到的。

在 ?src/app/? 文件夾中創(chuàng)建一個(gè)名叫 ?mock-heroes.ts? 的文件。定義一個(gè)包含十個(gè)英雄的常量數(shù)組 ?HEROES?,并導(dǎo)出它。該文件是這樣的。

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 ?類文件,并導(dǎo)入模擬的 ?HEROES?。

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

往類中添加一個(gè) ?heroes ?屬性,這樣可以暴露出這個(gè) ?HEROES ?數(shù)組,以供綁定。

export class HeroesComponent implements OnInit {

  heroes = HEROES;
}

使用 *ngFor 列出這些英雄

打開 ?HeroesComponent ?的模板文件,并做如下修改:

  1. 在頂部添加 ?<h2>?。
  2. 在它下面添加一個(gè) HTML 無序列表 ( ?<ul>? ) 元素。
  3. 在 ?<ul>? 中插入 ?<li>?。
  4. 在 ?<li>? 中放一個(gè) ?<button>? 元素,以便在 ?<span>? 元素中顯示單個(gè) ?hero ?的屬性。
  5. 點(diǎn)綴上一些 CSS 類(稍后你還會(huì)添加更多 CSS 樣式)。

做完之后應(yīng)該是這樣的:

<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' 不存在,因此會(huì)顯示一個(gè)錯(cuò)誤。要訪問每個(gè)英雄并列出所有英雄,請(qǐng)?jiān)?nbsp;?<li>? 上添加 ?*ngFor? 以遍歷英雄列表:

<li *ngFor="let hero of heroes">

?*ngFor? 是一個(gè) Angular 的復(fù)寫器(repeater)指令。它會(huì)為列表中的每項(xiàng)數(shù)據(jù)復(fù)寫它的宿主元素。

這個(gè)例子中涉及的語法如下:

語法

詳情

<li>

宿主元素。

heroes

來自 HeroesComponent 類的存放模擬(mock)英雄的列表。

hero

保存列表每次迭代的當(dāng)前 hero 對(duì)象。

不要忘了 ?ngFor ?前面的星號(hào)(?*?),它是該語法中的關(guān)鍵部分。

瀏覽器刷新之后,英雄列表出現(xiàn)了。

交互元素
注意:
在 ?<li>? 元素中,我們將英雄的詳細(xì)信息包裝在 ?<button>? 元素中。稍后我們使 hero 可點(diǎn)擊,并且出于無障礙性的目的,最好使用本機(jī)交互式 HTML 元素(例如 ?<button>?),而不是向非交互式元素添加事件偵聽器(例如 ?<li>?)。

給英雄列表“美容”

英雄列表應(yīng)該富有吸引力,并且當(dāng)用戶把鼠標(biāo)移到某個(gè)英雄上和從列表中選中某個(gè)英雄時(shí),應(yīng)該給出視覺反饋。

教程的第一章,你曾在 ?styles.css? 中為整個(gè)應(yīng)用設(shè)置了一些基礎(chǔ)的樣式。但那個(gè)樣式表并不包含英雄列表所需的樣式。

固然,你可以把更多樣式加入到 ?styles.css?,并且放任它隨著你添加更多組件而不斷膨脹。

但還有更好的方式。你可以定義屬于特定組件的私有樣式,并且讓組件所需的一切(代碼、HTML 和 CSS)都放在一起。

這種方式讓你在其它地方復(fù)用該組件更加容易,并且即使全局樣式和這里不一樣,組件也仍然具有期望的外觀。

你可以用多種方式定義私有樣式,或者內(nèi)聯(lián)在 ?@Component.styles? 數(shù)組中,或者在 ?@Component.styleUrls? 所指出的樣式表文件中。

當(dāng) CLI 生成 ?HeroesComponent ?時(shí),它也同時(shí)為 ?HeroesComponent ?創(chuàng)建了空白的 ?heroes.component.css? 樣式表文件,并且讓 ?@Component.styleUrls? 指向它,就像這樣。

@Component({
  selector: 'app-heroes',
  templateUrl: './heroes.component.html',
  styleUrls: ['./heroes.component.css']
})

打開 ?heroes.component.css? 文件,并且把 ?HeroesComponent ?的私有 CSS 樣式粘貼進(jìn)去。 

?@Component? 元數(shù)據(jù)中指定的樣式和樣式表都是局限于該組件的。?heroes.component.css? 中的樣式只會(huì)作用于 ?HeroesComponent?,既不會(huì)影響到組件外的 HTML,也不會(huì)影響到其它組件中的 HTML。

查看詳情

當(dāng)用戶在此列表中點(diǎn)擊一個(gè)英雄時(shí),該組件應(yīng)該在頁面底部顯示所選英雄的詳情。

在本節(jié),你將監(jiān)聽英雄條目的點(diǎn)擊事件,并顯示與更新英雄的詳情。

添加 click 事件綁定

為 ?<li>? 中的 ?<button>? 上添加一個(gè)點(diǎn)擊事件的綁定代碼:

<li *ngFor="let hero of heroes">
  <button type="button" (click)="onSelect(hero)">
  <!-- ... -->

?click ?外面的圓括號(hào)會(huì)讓 Angular 監(jiān)聽這個(gè) ?<button>? 元素的 ?click ?事件。 當(dāng)用戶點(diǎn)擊 ?<button>? 時(shí),Angular 就會(huì)執(zhí)行表達(dá)式 ?onSelect(hero)?。

下一部分,會(huì)在 ?HeroesComponent ?上定義一個(gè) ?onSelect()? 方法,用來顯示 ?*ngFor? 表達(dá)式所定義的那個(gè)英雄(?hero?)。

添加 click 事件處理器

把該組件的 ?hero ?屬性改名為 ?selectedHero?,但不要為它賦值。 因?yàn)閼?yīng)用剛剛啟動(dòng)時(shí)并沒有所選英雄。

添加如下 ?onSelect()? 方法,它會(huì)把模板中被點(diǎn)擊的英雄賦值給組件的 ?selectedHero ?屬性。

selectedHero?: Hero;
onSelect(hero: Hero): void {
  this.selectedHero = hero;
}

添加詳情區(qū)

現(xiàn)在,組件的模板中有一個(gè)列表。要想點(diǎn)擊列表中的一個(gè)英雄,并顯示該英雄的詳情,你需要在模板中留一個(gè)區(qū)域,用來顯示這些詳情。在 ?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>

只有在選擇英雄時(shí)才會(huì)顯示英雄詳細(xì)信息。最初創(chuàng)建組件時(shí),沒有所選的 hero,因此我們將 ?*ngIf? 指令添加到包裝 hero 詳細(xì)信息的 ?<div>? 中,以指示 Angular 僅在實(shí)際定義 ?selectedHero ?時(shí)(在它被通過點(diǎn)擊英雄來選擇)。

不要忘了 ?ngIf ?前面的星號(hào)(?*?),它是該語法中的關(guān)鍵部分。

為選定的英雄設(shè)置樣式

為了標(biāo)出選定的英雄,你可以在以前添加過的樣式中增加 CSS 類 ?.selected?。若要把 ?.selected? 類應(yīng)用于此 <li> 上,請(qǐng)使用類綁定。


Angular 的類綁定可以有條件地添加和刪除 CSS 類。只需將 ?[class.some-css-class]="some-condition"? 添加到要設(shè)置樣式的元素即可。

在 ?HeroesComponent ?模板中的 ?<button>? 元素上添加 ?[class.selected]? 綁定,代碼如下:

[class.selected]="hero === selectedHero"

如果當(dāng)前行的英雄和 ?selectedHero ?相同,Angular 就會(huì)添加 CSS 類 ?selected?,否則就會(huì)移除它。

最終的 ?<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;
    }

小結(jié)

  • 英雄之旅應(yīng)用在一個(gè)主從視圖中顯示了英雄列表。
  • 用戶可以選擇一個(gè)英雄,并查看該英雄的詳情。
  • 你使用 ?*ngFor? 顯示了一個(gè)列表。
  • 你使用 ?*ngIf? 來根據(jù)條件包含或排除了一段 HTML。
  • 你可以用 ?class ?綁定來切換 CSS 的樣式類。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)