在此頁面中,您將擴展「Tour of Heroes」應用程序,以顯示英雄列表,並容許用戶選擇英雄並顯示英雄的詳細信息。css
完成此頁面後,該應用應該看起來像這個實例(查看源代碼)。html
在繼續本「英雄之旅」頁面以前,請確認您在「英雄編輯器」頁面以後具備如下結構。 若是您的結構不匹配,請返回該頁面以弄清楚您錯過了什麼。java
若是該應用還沒有運行,請啓動該應用。 當您進行更改時,請經過從新加載瀏覽器窗口來保持運行。git
在添加新功能以前,您能夠從應用程序重構中受益。web
您將對應用程序組件的模板進行屢次更新。 首先,將模板移動到本身的文件中:api
lib/app_component.html瀏覽器
<h1>{{title}}</h1> <h2>{{hero.name}} details!</h2> <div><label>id: </label>{{hero.id}}</div> <div> <label>name: </label> <input [(ngModel)]="hero.name" placeholder="name"> </div>
用指向新模板文件的templateUrl替換@Component模板:app
lib/app_component.dart (metadata)編輯器
@Component( selector: 'my-app', templateUrl: 'app_component.html', directives: const [formDirectives], )
刷新瀏覽器。 該應用仍然繼續運行。ide
分開使用並將Hero類從app_component.dart 中移動到它本身的文件中,建立lib/src文件夾來裝Hero源文件:lib/src/hero.dart
class Hero { final int id; String name; Hero(this.id, this.name); }
回到應用程序組件中,使用相對路徑添加一個import 到新建立的文件:lib/app_component.dart (hero import)
import 'src/hero.dart';
刷新瀏覽器,程序將正常運行,如今添加新功能的準備工做就作好了。
顯示一個英雄列表,首先須要將英雄們添加到視圖模板
在lib / src下的如下文件中建立十個英雄的列表:lib/src/mock_heroes.dart
import 'hero.dart'; final mockHeroes = <Hero>[ new Hero(11, 'Mr. Nice'), new Hero(12, 'Narco'), new Hero(13, 'Bombasto'), new Hero(14, 'Celeritas'), new Hero(15, 'Magneta'), new Hero(16, 'RubberMan'), new Hero(17, 'Dynama'), new Hero(18, 'Dr IQ'), new Hero(19, 'Magma'), new Hero(20, 'Tornado') ];
最終這個應用程序將從Web服務獲取英雄列表,但如今你能夠顯示模擬英雄。
將hero字段替換爲AppComponent中的公共heros字段,並使用模擬英雄進行初始化(不要忘記導入):lib/app_component.dart (heroes)
import 'src/mock_heroes.dart'; // ··· class AppComponent { final title = 'Tour of Heroes'; List<Hero> heroes = mockHeroes; // ··· }
英雄數據與類實現分開,由於英雄名字最終未來自數據服務。
要在無序列表中顯示英雄名稱,請將全部當前模板替換爲如下HTML:lib/app_component.html (heroes template)
<h1>{{title}}</h1> <h2>My Heroes</h2> <ul class="heroes"> <li> <!-- each hero goes here --> </li> </ul>
下一步你將添加英雄名字
目標是將組件中的英雄列表綁定到模板,迭代它們,並單獨顯示它們。經過添加核心指令* ngFor修改<li>標籤。
<li *ngFor="let hero of heroes">
ngFor的前綴(*)是此語法的關鍵部分。 它表示<li>元素及其子元素構成一個主模板。
ngFor指令遍歷組件的英雄列表併爲該列表中的每一個英雄呈現該模板的一個實例。
表達式部分將hero標識爲模板輸入變量,其中包含每一個迭代的英雄詳情。
你能夠在模板中引用這個變量來訪問當前英雄的屬性。
在顯示數據的Showing a list property with *ngFor部分閱讀更多關於ngFor和模板輸入變量和模板語法頁的ngFor部分
在<li>節點內添加內容hero模板變量來顯示英雄屬性 lib/app_component.html (ngFor)
<li *ngFor="let hero of heroes"> <span class="badge">{{hero.id}}</span> {{hero.name}} </li>
要在模板中使用Angular指令,須要在組件的@Component註解的指令參數中列出。 與您在第1部分中所作的類似,添加全部:CORE_DIRECTIVES:
CORE_DIRECTIVES = const [NgClass, NgFor, NgIf, NgTemplateOutlet, NgStyle, NgSwitch, NgSwitchWhen, NgSwitchDefault]
lib/app_component.dart (directives)
@Component( selector: 'my-app', // ··· directives: const [CORE_DIRECTIVES, formDirectives], )
刷新瀏覽器,英雄列表將顯示出來。
用戶應該獲得一個他們徘徊和被選中英雄的視覺提示。要爲組件添加樣式,能夠設置@Component註解的styles參數:lib/app_component.dart (styles)
// Not recommended when adding many CSS classes: styles: const [ ''' .selected { ... } .heroes { ... } ... ''' ],
可是,當添加多個樣式時,這使得Dart文件更長,更不易讀。 而應將樣式放在.css文件中,並使用@Component的styleUrls參數引用該文件。 按照慣例,組件的CSS和Dart文件的名稱具備相同的基礎命名前綴(app_component)。lib/app_component.dart (stylUrls)
@Component( selector: 'my-app', // ··· styleUrls: const ['app_component.css'], // ··· )
lib/app_component.css
.selected { background-color: #CFD8DC !important; color: white; } .heroes { margin: 0 0 2em 0; list-style-type: none; padding: 0; width: 15em; } .heroes li { cursor: pointer; position: relative; left: 0; background-color: #EEE; margin: .5em; padding: .3em 0; height: 1.6em; border-radius: 4px; } .heroes li.selected:hover { color: white; } .heroes li:hover { color: #607D8B; background-color: #EEE; left: .1em; } .heroes .text { position: relative; top: -3px; } .heroes .badge { display: inline-block; font-size: small; color: white; padding: 0.8em 0.7em 0 0.7em; background-color: #607D8B; line-height: 1em; position: relative; left: -1px; top: -4px; height: 1.8em; margin-right: .8em; border-radius: 4px 0 0 4px; }
將樣式分配給組件時,它們的做用域爲該特定組件。 這些樣式僅適用於AppComponent,不影響外部HTML。
顯示英雄的模板應該是這樣的:lib/app_component.html (styled heroes)
<h2>My Heroes</h2> <ul class="heroes"> <li *ngFor="let hero of heroes"> <span class="badge">{{hero.id}}</span> {{hero.name}} </li> </ul>
該應用程序如今顯示英雄列表,以及在詳細信息視圖中的單個英雄。 可是列表和細節視圖沒有鏈接。 當用戶從列表中選擇一個英雄時,選擇的英雄應該出如今細節視圖中。 這個UI模式被稱爲「主/細節」。在這種狀況下,主人是英雄列表,細節是選擇的英雄。
接下來,您將經過selectedHero組件屬性將主連接到詳細信息,該屬性綁定到單擊事件。
添加點擊事件綁定到<li>:lib/app_component.html (click)
<li *ngFor="let hero of heroes" (click)="onSelect(hero)"> <span class="badge">{{hero.id}}</span> {{hero.name}} </li>
圓括號將<li>元素的點擊事件標識爲目標。 onSelect(hero)表達式調用AppComponent方法onSelect(),傳遞模板輸入變量hero做爲參數。 這是你在ngFor指令中定義的同一個英雄變量。
你再也不須要hero屬性,由於你再也不顯示一個英雄; 你正在顯示一個英雄列表。 可是用戶能夠經過點擊選擇一個英雄。 因此用這個簡單的selectedHero屬性來替換hero屬性:lib/app_component.dart (selectedHero)
Hero selectedHero;
在用戶選擇英雄以前,全部的英雄名字都應該被取消選擇,因此你不會像hero同樣初始化selectedHero。添加一個onSelect()方法,將selectedHero屬性設置爲用戶單擊的英雄。lib/app_component.dart (onSelect)
void onSelect(Hero hero) => selectedHero = hero;
該模板仍然是指舊的英雄屬性。 綁定到新的selectedHero屬性,以下所示:lib/app_component.html (selectedHero details)
<h2>{{selectedHero.name}} details!</h2> <div><label>id: </label>{{selectedHero.id}}</div> <div> <label>name: </label> <input [(ngModel)]="selectedHero.name" placeholder="name"> </div>
當應用程序加載時,selectedHero爲null。 當用戶點擊英雄的名字時,所選擇的英雄被初始化。 Angular沒法顯示null selectedHero的屬性並拋出如下錯誤,在瀏覽器的控制檯中可見:
EXCEPTION: TypeError: Cannot read property 'name' of undefined in [null]
儘管selectedHero.name顯示在模板中,但必須保留DOM外的英雄詳細信息,直到出現選定的英雄。用<div>包裝模板的HTML英雄細節內容。 而後添加ngIf核心指令並將其設置爲selectedHero!= null。lib/app_component.html (ngIf)
<div *ngIf="selectedHero != null"> <h2>{{selectedHero.name}} details!</h2> <div><label>id: </label>{{selectedHero.id}}</div> <div> <label>name: </label> <input [(ngModel)]="selectedHero.name" placeholder="name"> </div> </div>
不要忘記ngIf前面的星號(*)。
刷新瀏覽器,該應用程序再也不失敗,名稱列表再次顯示在瀏覽器中。
當沒有選定的英雄時,ngIf指令從DOM中移除英雄詳情HTML。 沒有英雄細節元素或綁定擔憂。
當用戶選擇一個英雄時,selectedHero變爲非null,ngIf將英雄詳細內容放入DOM中,並評估嵌套的綁定。
當選擇的英雄細節顯示在列表下方時,很難在列表中識別選定的英雄。
在上面添加的樣式元數據中,有一個名爲selected的自定義CSS類。 爲了讓選定的英雄更清晰可見,當用戶點擊英雄名字時,你將把這個選定的class應用到<li>。 例如,當用戶點擊「Magneta」時,它應該用一個獨特但微妙的背景顏色渲染,以下所示:
在模板中,將如下綁定添加到<li>標記中:
[class.selected]="hero === selectedHero"
當表達式(hero === selectedHero)爲true時,Angular將添加所選的CSS類。 當表達式爲false時,Angular刪除選定的類。
===運算符測試給定的對象是否相同。
在模板語法指南中閱讀有關[class]綁定的更多信息。
<li>的最終版本以下所示:lib/app_component.html (ngFor with class.selected)
<li *ngFor="let hero of heroes" [class.selected]="hero === selectedHero" (click)="onSelect(hero)"> <span class="badge">{{hero.id}}</span> {{hero.name}} </li>
點擊「Magneta」後,列表應該以下所示:
您的項目應該有如下文件:
教程組件測試
本教程不包括測試,可是若是您查看示例代碼,則會爲本教程添加的每一個新功能進行組件測試。 詳細信息請參閱組件測試頁面。
如下是您在此頁面中所取得的成果:
英雄之旅(Tour of Heroes)應用程序顯示可選英雄列表。
您將應用程序模板移到了本身的文件中。
您將Hero類移到lib / src下的本身的文件中。
你增長了選擇英雄和顯示英雄的細節的能力。
您瞭解瞭如何在組件模板中使用核心指令ngIf和ngFor。
您在CSS文件中定義了樣式,並使用它們來設置應用程序的樣式。
你的應用應該看起來像這個實例(查看源代碼)。
你已經擴大了英雄之旅的應用程序,但它還遠遠沒有完成。 一個應用程序不該該是一個單一的組件。 在下一頁中,您將將應用程序拆分爲子組件,並使它們一塊兒工做。