英雄編輯器
The hero editor
應用程式現在有了基本的標題。 接下來你要建立一個新的元件來顯示英雄資訊並且把這個元件放到應用程式的外殼裡去。
The application now has a basic title. Next you will create a new component to display hero information and place that component in the application shell.
建立英雄列表元件
Create the heroes component
使用 Angular CLI 建立一個名為 heroes
的新元件。
Using the Angular CLI, generate a new component named heroes
.
ng generate component heroes
CLI 建立了一個新的資料夾 src/app/heroes/
,並生成了 HeroesComponent
的四個檔案。
The CLI creates a new folder, src/app/heroes/
, and generates the three files of the HeroesComponent
along with a test file.
HeroesComponent
的類別檔案如下:
The HeroesComponent
class file is as follows:
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-heroes',
templateUrl: './heroes.component.html',
styleUrls: ['./heroes.component.css']
})
export class HeroesComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
你要從 Angular 核心函式庫中匯入 Component
符號,並為元件類別加上 @Component
裝飾器。
You always import the Component
symbol from the Angular core library and annotate the component class with @Component
.
@Component
是個裝飾器函式,用於為該元件指定 Angular 所需的元資料。
@Component
is a decorator function that specifies the Angular metadata for the component.
CLI 自動生成了三個元資料屬性:
The CLI generated three metadata properties:
selector
— 元件的選擇器(CSS 元素選擇器)selector
— the component's CSS element selectortemplateUrl
— 元件範本檔案的位置。templateUrl
— the location of the component's template file.styleUrls
— 元件私有 CSS 樣式表文件的位置。styleUrls
— the location of the component's private CSS styles.
CSS 元素選擇器 app-heroes
用來在父元件的範本中匹配 HTML 元素的名稱,以識別出該元件。
The CSS element selector, 'app-heroes'
, matches the name of the HTML element that identifies this component within a parent component's template.
ngOnInit()
是一個生命週期鉤子,Angular 在建立完元件後很快就會呼叫 ngOnInit()
。這裡是放置初始化邏輯的好地方。
The ngOnInit()
is a lifecycle hook. Angular calls ngOnInit()
shortly after creating a component. It's a good place to put initialization logic.
始終要 export
這個元件類別,以便在其它地方(比如 AppModule
)匯入它。
Always export
the component class so you can import
it elsewhere ... like in the AppModule
.
新增 hero
屬性
Add a hero
property
往 HeroesComponent
中新增一個 hero
屬性,用來表示一個名叫 “Windstorm” 的英雄。
Add a hero
property to the HeroesComponent
for a hero named "Windstorm."
hero = 'Windstorm';
顯示英雄
Show the hero
開啟範本檔案 heroes.component.html
。刪除 Angular CLI 自動產生的預設內容,改為到 hero
屬性的資料繫結。
Open the heroes.component.html
template file. Delete the default text generated by the Angular CLI and replace it with a data binding to the new hero
property.
<h2>{{hero}}</h2>
顯示 HeroesComponent
檢視
Show the HeroesComponent
view
要顯示 HeroesComponent
你必須把它加到殼元件 AppComponent
的範本中。
To display the HeroesComponent
, you must add it to the template of the shell AppComponent
.
別忘了,app-heroes
就是 HeroesComponent
的 元素選擇器。 所以,只要把 <app-heroes>
元素新增到 AppComponent
的範本檔案中就可以了,就放在標題下方。
Remember that app-heroes
is the element selector for the HeroesComponent
. So add an <app-heroes>
element to the AppComponent
template file, just below the title.
<h1>{{title}}</h1>
<app-heroes></app-heroes>
如果 CLI 的 ng serve
命令仍在執行,瀏覽器就會自動重新整理,並且同時顯示出應用的標題和英雄的名字。
Assuming that the CLI ng serve
command is still running, the browser should refresh and display both the application title and the hero name.
建立 Hero
類別
Create a Hero interface
真實的英雄當然不止一個名字。
A real hero is more than a name.
在 src/app
資料夾中為 Hero
類別建立一個檔案,並新增 id
和 name
屬性。
Create a Hero
interface in its own file in the src/app
folder. Give it id
and name
properties.
export interface Hero {
id: number;
name: string;
}
回到 HeroesComponent
類別,並且匯入這個 Hero
類別。
Return to the HeroesComponent
class and import the Hero
interface.
把元件的 hero
屬性的型別重構為 Hero
。 然後以 1
為 id
、以 “Windstorm” 為名字初始化它。
Refactor the component's hero
property to be of type Hero
. Initialize it with an id
of 1
and the name Windstorm
.
修改後的 HeroesComponent
類別應該是這樣的:
The revised HeroesComponent
class file should look like this:
import { Component, OnInit } from '@angular/core';
import { Hero } from '../hero';
@Component({
selector: 'app-heroes',
templateUrl: './heroes.component.html',
styleUrls: ['./heroes.component.css']
})
export class HeroesComponent implements OnInit {
hero: Hero = {
id: 1,
name: 'Windstorm'
};
constructor() { }
ngOnInit() {
}
}
頁面顯示變得不正常了,因為你剛剛把 hero
從字串改成了物件。
The page no longer displays properly because you changed the hero from a string to an object.
顯示 hero
物件
Show the hero object
修改範本中的繫結,以顯示英雄的名字,並在詳情中顯示 id
和 name
,就像這樣:
Update the binding in the template to announce the hero's name and show both id
and name
in a details layout like this:
<h2>{{hero.name}} Details</h2>
<div><span>id: </span>{{hero.id}}</div>
<div><span>name: </span>{{hero.name}}</div>
瀏覽器自動重新整理,並顯示這位英雄的資訊。
The browser refreshes and displays the hero's information.
使用 UppercasePipe
進行格式化
Format with the UppercasePipe
把 hero.name
的繫結修改成這樣:
Modify the hero.name
binding like this.
<h2>{{hero.name | uppercase}} Details</h2>
瀏覽器重新整理了。現在,英雄的名字顯示成了大寫字母。
The browser refreshes and now the hero's name is displayed in capital letters.
繫結表示式中的 uppercase
位於管道運算子( | )的右邊,用來呼叫內建管道 UppercasePipe
。
The word uppercase
in the interpolation binding, right after the pipe operator ( | ), activates the built-in UppercasePipe
.
管道 是格式化字串、金額、日期和其它顯示資料的好辦法。 Angular 發佈了一些內建管道,而且你還可以建立自己的管道。
Pipes are a good way to format strings, currency amounts, dates and other display data. Angular ships with several built-in pipes and you can create your own.
編輯英雄名字
Edit the hero
使用者應該能在一個 <input>
輸入框中編輯英雄的名字。
Users should be able to edit the hero name in an <input>
textbox.
當用戶輸入時,這個輸入框應該能同時顯示和修改英雄的 name
屬性。 也就是說,資料流從元件類別流出到螢幕,並且從螢幕流回到元件類別。
The textbox should both display the hero's name
property and update that property as the user types. That means data flows from the component class out to the screen and from the screen back to the class.
要想讓這種資料流動自動化,就要在表單元素 <input>
和元件的 hero.name
屬性之間建立雙向資料繫結。
To automate that data flow, setup a two-way data binding between the <input>
form element and the hero.name
property.
雙向繫結
Two-way binding
把範本中的英雄詳情區重構成這樣:
Refactor the details area in the HeroesComponent
template so it looks like this:
<div>
<label for="name">Hero name: </label>
<input id="name" [(ngModel)]="hero.name" placeholder="name">
</div>
[(ngModel)] 是 Angular 的雙向資料繫結語法。
[(ngModel)] is Angular's two-way data binding syntax.
這裡把 hero.name
屬性繫結到了 HTML 的 textbox 元素上,以便資料流可以雙向流動:從 hero.name
屬性流動到 textbox,並且從 textbox 流回到 hero.name
。
Here it binds the hero.name
property to the HTML textbox so that data can flow in both directions: from the hero.name
property to the textbox, and from the textbox back to the hero.name
.
缺少 FormsModule
The missing FormsModule
注意,當你加上 [(ngModel)]
之後這個應用無法工作了。
Notice that the app stopped working when you added [(ngModel)]
.
開啟瀏覽器的開發工具,就會在控制檯中看到如下資訊:
To see the error, open the browser development tools and look in the console for a message like
Template parse errors:
Can't bind to 'ngModel' since it isn't a known property of 'input'.
雖然 ngModel
是一個有效的 Angular 指令,不過它在預設情況下是不可用的。
Although ngModel
is a valid Angular directive, it isn't available by default.
它屬於一個可選模組 FormsModule
,你必須自行新增此模組才能使用該指令。
It belongs to the optional FormsModule
and you must opt-in to using it.
AppModule
Angular 需要知道如何把應用程式的各個部分組合到一起,以及該應用需要哪些其它檔案和函式庫。 這些資訊被稱為元資料(metadata)。
Angular needs to know how the pieces of your application fit together and what other files and libraries the app requires. This information is called metadata.
有些元資料位於 @Component
裝飾器中,你會把它加到元件類別上。 另一些關鍵性的元資料位於 @NgModule
裝飾器中。
Some of the metadata is in the @Component
decorators that you added to your component classes. Other critical metadata is in @NgModule
decorators.
最重要的 @NgModule
裝飾器位於最上層類別 AppModule 上。
The most important @NgModule
decorator annotates the top-level AppModule class.
Angular CLI 在建立專案的時候就在 src/app/app.module.ts
中生成了一個 AppModule
類別。 這裡也就是你要新增 FormsModule
的地方。
The Angular CLI generated an AppModule
class in src/app/app.module.ts
when it created the project. This is where you opt-in to the FormsModule
.
匯入 FormsModule
Import FormsModule
開啟 AppModule
(app.module.ts
) 並從 @angular/forms
函式庫中匯入 FormsModule
符號。
Open AppModule
(app.module.ts
) and import the FormsModule
symbol from the @angular/forms
library.
import { FormsModule } from '@angular/forms'; // <-- NgModel lives here
然後把 FormsModule
新增到 @NgModule
元資料的 imports
陣列中,這裡是該應用所需外部模組的列表。
Then add FormsModule
to the @NgModule
metadata's imports
array, which contains a list of external modules that the app needs.
imports: [
BrowserModule,
FormsModule
],
重新整理瀏覽器,應用又能正常工作了。你可以編輯英雄的名字,並且會看到這個改動立刻體現在這個輸入框上方的 <h2>
中。
When the browser refreshes, the app should work again. You can edit the hero's name and see the changes reflected immediately in the <h2>
above the textbox.
宣告 HeroesComponent
Declare HeroesComponent
每個元件都必須宣告在(且只能宣告在)一個 NgModule 中。
Every component must be declared in exactly one NgModule.
你沒有宣告過 HeroesComponent
,可為什麼本應用卻正常呢?
You didn't declare the HeroesComponent
. So why did the application work?
這是因為 Angular CLI 在產生 HeroesComponent
元件的時候就自動把它加到了 AppModule
中。
It worked because the Angular CLI declared HeroesComponent
in the AppModule
when it generated that component.
開啟 src/app/app.module.ts
你就會發現 HeroesComponent
已經在頂部匯入過了。
Open src/app/app.module.ts
and find HeroesComponent
imported near the top.
import { HeroesComponent } from './heroes/heroes.component';
HeroesComponent
也已經宣告在了 @NgModule.declarations
陣列中。
The HeroesComponent
is declared in the @NgModule.declarations
array.
declarations: [
AppComponent,
HeroesComponent
],
注意 AppModule
聲明瞭應用中的所有元件,AppComponent
和 HeroesComponent
。
Note that AppModule
declares both application components, AppComponent
and HeroesComponent
.
檢視最終程式碼
Final code review
應用跑起來應該是這樣的:
Your app should look like this
import { Component, OnInit } from '@angular/core';
import { Hero } from '../hero';
@Component({
selector: 'app-heroes',
templateUrl: './heroes.component.html',
styleUrls: ['./heroes.component.css']
})
export class HeroesComponent implements OnInit {
hero: Hero = {
id: 1,
name: 'Windstorm'
};
constructor() { }
ngOnInit() {
}
}
小結
Summary
你使用 CLI 建立了第二個元件
HeroesComponent
。You used the CLI to create a second
HeroesComponent
.你把
HeroesComponent
新增到了殼元件AppComponent
中,以便顯示它。You displayed the
HeroesComponent
by adding it to theAppComponent
shell.你使用
UppercasePipe
來格式化英雄的名字。You applied the
UppercasePipe
to format the name.你用
ngModel
指令實現了雙向資料繫結。You used two-way data binding with the
ngModel
directive.你知道了
AppModule
。You learned about the
AppModule
.你把
FormsModule
匯入了AppModule
,以便 Angular 能識別並應用ngModel
指令。You imported the
FormsModule
in theAppModule
so that Angular would recognize and apply thengModel
directive.你知道了把元件宣告到
AppModule
是很重要的,並認識到 CLI 會自動幫你宣告它。You learned the importance of declaring components in the
AppModule
and appreciated that the CLI declared it for you.