填寫這份《一分鐘調查》,幫我們(開發組)做得更好!去填寫Home

主從元件

Create a feature component

此刻,HeroesComponent 同時顯示了英雄列表和所選英雄的詳情。

At the moment, the HeroesComponent displays both the list of heroes and the selected hero's details.

把所有特性都放在同一個元件中,將會使應用“長大”後變得不可維護。 你要把大型元件拆分成小一點的子元件,每個子元件都要集中精力處理某個特定的任務或工作流。

Keeping all features in one component as the application grows will not be maintainable. You'll want to split up large components into smaller sub-components, each focused on a specific task or workflow.

本頁面中,你將邁出第一步 —— 把英雄詳情移入一個獨立的、可複用的 HeroDetailComponent

In this page, you'll take the first step in that direction by moving the hero details into a separate, reusable HeroDetailComponent.

HeroesComponent 將僅僅用來表示英雄列表。 HeroDetailComponent 將用來表示所選英雄的詳情。

The HeroesComponent will only present the list of heroes. The HeroDetailComponent will present details of a selected hero.

要檢視本頁所講的範例程式,參閱現場演練 / 下載範例

For the sample application that this page describes, see the現場演練 / 下載範例.

製作 HeroDetailComponent

Make the HeroDetailComponent

使用 Angular CLI 產生一個名叫 hero-detail 的新元件。

Use the Angular CLI to generate a new component named hero-detail.

      
      ng generate component hero-detail
    

這個命令會做這些事:

The command scaffolds the following:

  • 建立目錄 src/app/hero-detail

    Creates a directory src/app/hero-detail.

在這個目錄中會產生四個檔案:

Inside that directory four files are generated:

  • 作為元件樣式的 CSS 檔案。

    A CSS file for the component styles.

  • 作為元件範本的 HTML 檔案。

    An HTML file for the component template.

  • 存放元件類別 HeroDetailComponent 的 TypeScript 檔案。

    A TypeScript file with a component class named HeroDetailComponent.

  • HeroDetailComponent 類別的測試檔案。

    A test file for the HeroDetailComponent class.

該命令還會把 HeroDetailComponent 新增到 src/app/app.module.ts 檔案中 @NgModuledeclarations 列表中。

The command also adds the HeroDetailComponent as a declaration in the @NgModule decorator of the src/app/app.module.ts file.

編寫範本

Write the template

HeroesComponent 範本的底部把表示英雄詳情的 HTML 程式碼剪下貼上到所產生的 HeroDetailComponent 範本中。

Cut the HTML for the hero detail from the bottom of the HeroesComponent template and paste it over the generated boilerplate in the HeroDetailComponent template.

所貼上的 HTML 參考了 selectedHero。 新的 HeroDetailComponent 可以展示任意英雄,而不僅僅所選的。因此還要把範本中的所有 selectedHero 替換為 hero

The pasted HTML refers to a selectedHero. The new HeroDetailComponent can present any hero, not just a selected hero. So replace "selectedHero" with "hero" everywhere in the template.

完工之後,HeroDetailComponent 的範本應該是這樣的:

When you're done, the HeroDetailComponent template should look like this:

src/app/hero-detail/hero-detail.component.html
      
      <div *ngIf="hero">

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

</div>
    

新增 @Input() hero 屬性

Add the @Input() hero property

HeroDetailComponent 範本中綁定了元件中的 hero 屬性,它的型別是 Hero

The HeroDetailComponent template binds to the component's hero property which is of type Hero.

開啟 HeroDetailComponent 類別檔案,並匯入 Hero 符號。

Open the HeroDetailComponent class file and import the Hero symbol.

src/app/hero-detail/hero-detail.component.ts (import Hero)
      
      import { Hero } from '../hero';
    

hero 屬性必須是一個帶有 @Input() 裝飾器的輸入屬性,因為外部的 HeroesComponent 元件將會繫結到它。就像這樣:

The hero property must be an Input property, annotated with the @Input() decorator, because the external HeroesComponent will bind to it like this.

      
      <app-hero-detail [hero]="selectedHero"></app-hero-detail>
    

修改 @angular/core 的匯入語句,匯入 Input 符號。

Amend the @angular/core import statement to include the Input symbol.

src/app/hero-detail/hero-detail.component.ts (import Input)
      
      import { Component, OnInit, Input } from '@angular/core';
    

新增一個帶有 @Input() 裝飾器的 hero 屬性。

Add a hero property, preceded by the @Input() decorator.

src/app/hero-detail/hero-detail.component.ts
      
      @Input() hero?: Hero;
    

這就是你要對 HeroDetailComponent 類別做的唯一一項修改。 沒有其它屬性,也沒有展示邏輯。這個元件所做的只是透過 hero 屬性接收一個英雄物件,並顯示它。

That's the only change you should make to the HeroDetailComponent class. There are no more properties. There's no presentation logic. This component only receives a hero object through its hero property and displays it.

顯示 HeroDetailComponent

Show the HeroDetailComponent

HeroesComponent 會自行顯示英雄的詳情,但後面我們要移除這部分。本節會指導你把這部分邏輯委派給 HeroDetailComponent

The HeroesComponent used to display the hero details on its own, before you removed that portion of the template. This section guides you through delegating logic to the HeroDetailComponent.

這兩個元件將會具有父子關係。 當用戶從列表中選擇了某個英雄時,父元件 HeroesComponent 將透過把要顯示的新英雄傳送給子元件 HeroDetailComponent,來控制子元件。

The two components will have a parent/child relationship. The parent HeroesComponent will control the child HeroDetailComponent by sending it a new hero to display whenever the user selects a hero from the list.

你不用修改 HeroesComponent 類別,但是要修改它的範本

You won't change the HeroesComponent class but you will change its template.

修改 HeroesComponent 的範本

Update the HeroesComponent template

HeroDetailComponent 的選擇器是 'app-hero-detail'。 把 <app-hero-detail> 新增到 HeroesComponent 範本的底部,以便把英雄詳情的檢視顯示到那裡。

The HeroDetailComponent selector is 'app-hero-detail'. Add an <app-hero-detail> element near the bottom of the HeroesComponent template, where the hero detail view used to be.

HeroesComponent.selectedHero 繫結到該元素的 hero 屬性,就像這樣:

Bind the HeroesComponent.selectedHero to the element's hero property like this.

heroes.component.html (HeroDetail binding)
      
      <app-hero-detail [hero]="selectedHero"></app-hero-detail>
    

[hero]="selectedHero" 是 Angular 的屬性繫結語法。

[hero]="selectedHero" is an Angular property binding.

這是一種單向資料繫結。從 HeroesComponentselectedHero 屬性繫結到目標元素的 hero 屬性,並對映到了 HeroDetailComponenthero 屬性。

It's a one way data binding from the selectedHero property of the HeroesComponent to the hero property of the target element, which maps to the hero property of the HeroDetailComponent.

現在,當用戶在列表中點選某個英雄時,selectedHero 就改變了。 當 selectedHero 改變時,屬性繫結會修改 HeroDetailComponenthero 屬性,HeroDetailComponent 就會顯示這個新的英雄。

Now when the user clicks a hero in the list, the selectedHero changes. When the selectedHero changes, the property binding updates hero and the HeroDetailComponent displays the new hero.

修改後的 HeroesComponent 的範本是這樣的:

The revised HeroesComponent template should look like this:

heroes.component.html
      
      <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>

<app-hero-detail [hero]="selectedHero"></app-hero-detail>
    

瀏覽器重新整理,應用又像以前一樣開始工作了。

The browser refreshes and the application starts working again as it did before.

有哪些變化?

What changed?

以前一樣,一旦使用者點選了一個英雄的名字,該英雄的詳情就顯示在了英雄列表下方。 現在,HeroDetailComponent 負責顯示那些詳情,而不再是 HeroesComponent

As before, whenever a user clicks on a hero name, the hero detail appears below the hero list. Now the HeroDetailComponent is presenting those details instead of the HeroesComponent.

把原來的 HeroesComponent 重構成兩個元件帶來了一些優點,無論是現在還是未來:

Refactoring the original HeroesComponent into two components yields benefits, both now and in the future:

  1. 你透過縮減 HeroesComponent 的職責縮小了該元件。

    You reduced the HeroesComponent responsibilities.

  2. 你可以把 HeroDetailComponent 改進成一個功能豐富的英雄編輯器,而不用改動父元件 HeroesComponent

    You can evolve the HeroDetailComponent into a rich hero editor without touching the parent HeroesComponent.

  3. 你可以改進 HeroesComponent,而不用改動英雄詳情檢視。

    You can evolve the HeroesComponent without touching the hero detail view.

  4. 將來你可以在其它元件的範本中重複使用 HeroDetailComponent

    You can re-use the HeroDetailComponent in the template of some future component.

檢視最終程式碼

Final code review

本頁討論的程式碼檔案如下。

Here are the code files discussed on this page.

      
      import { Component, OnInit, Input } from '@angular/core';
import { Hero } from '../hero';

@Component({
  selector: 'app-hero-detail',
  templateUrl: './hero-detail.component.html',
  styleUrls: ['./hero-detail.component.css']
})
export class HeroDetailComponent implements OnInit {
  @Input() hero?: Hero;

  constructor() { }

  ngOnInit() {
  }

}
    

小結

Summary

  • 你建立了一個獨立的、可複用的 HeroDetailComponent 元件。

    You created a separate, reusable HeroDetailComponent.

  • 你用屬性繫結語法來讓父元件 HeroesComponent 可以控制子元件 HeroDetailComponent

    You used a property binding to give the parent HeroesComponent control over the child HeroDetailComponent.

  • 你用 @Input 裝飾器來讓 hero 屬性可以在外部的 HeroesComponent 中繫結。

    You used the @Input decorator to make the hero property available for binding by the external HeroesComponent.