有一些《英雄之旅》的新需求:
要查看本頁(yè)所講的范例程序,參閱現(xiàn)場(chǎng)演練 / 下載范例。
完成時(shí),用戶就能像這樣在應(yīng)用中導(dǎo)航:
在 Angular 中,最好在一個(gè)獨(dú)立的頂層模塊中加載和配置路由器,它專注于路由功能,然后由根模塊 ?AppModule
?導(dǎo)入它。
按照慣例,這個(gè)模塊類的名字叫做 ?AppRoutingModule
?,并且位于 ?src/app
? 下的 ?app-routing.module.ts
? 文件中。
使用 CLI 生成它。
ng generate module app-routing --flat --module=app
參數(shù) |
詳情 |
---|---|
--flat
|
把這個(gè)文件放進(jìn)了 |
--module=app
|
告訴 CLI 把它注冊(cè)到 |
生成的文件是這樣的:
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
@NgModule({
imports: [
CommonModule
],
declarations: []
})
export class AppRoutingModule { }
把它替換為如下代碼:
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HeroesComponent } from './heroes/heroes.component';
const routes: Routes = [
{ path: 'heroes', component: HeroesComponent }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
首先,?app-routing.module.ts
? 會(huì)導(dǎo)入 ?RouterModule
?和 ?Routes
?,以便該應(yīng)用具有路由功能。配置好路由后,接著導(dǎo)入 ?HeroesComponent
?,它將告訴路由器要去什么地方。
注意,對(duì) ?CommonModule
?的引用和 ?declarations
?數(shù)組不是必要的,因此它們不再是 ?AppRoutingModule
?的一部分。以下各節(jié)將詳細(xì)介紹 ?AppRoutingModule
?的其余部分。
該文件的下一部分是你的路由配置。Routes 告訴路由器,當(dāng)用戶單擊鏈接或?qū)?nbsp;URL 粘貼進(jìn)瀏覽器地址欄時(shí)要顯示哪個(gè)視圖。
由于 ?app-routing.module.ts
? 已經(jīng)導(dǎo)入了 ?HeroesComponent
?,因此你可以直接在 ?routes
?數(shù)組中使用它:
const routes: Routes = [
{ path: 'heroes', component: HeroesComponent }
];
典型的 Angular ?Route
?具有兩個(gè)屬性:
屬性 |
詳情 |
---|---|
path
|
用來(lái)匹配瀏覽器地址欄中 URL 的字符串。 |
component
|
導(dǎo)航到該路由時(shí),路由器應(yīng)該創(chuàng)建的組件。 |
這會(huì)告訴路由器把該 URL 與 ?path:'heroes'
? 匹配。如果網(wǎng)址類似于 ?localhost:4200/heroes
? 就顯示 ?HeroesComponent
?。
?@NgModule
? 元數(shù)據(jù)會(huì)初始化路由器,并開(kāi)始監(jiān)聽(tīng)瀏覽器地址的變化。
下面的代碼行將 ?RouterModule
?添加到 ?AppRoutingModule
?的 ?imports
?數(shù)組中,同時(shí)通過(guò)調(diào)用 ?RouterModule.forRoot()
? 來(lái)用這些 ?routes
?配置它:
imports: [ RouterModule.forRoot(routes) ],
這個(gè)方法之所以叫 ?
forRoot()
?,是因?yàn)槟阋趹?yīng)用的頂層配置這個(gè)路由器。?forRoot()
? 方法會(huì)提供路由所需的服務(wù)提供者和指令,還會(huì)基于瀏覽器的當(dāng)前 URL 執(zhí)行首次導(dǎo)航。
接下來(lái),?AppRoutingModule
?導(dǎo)出 ?RouterModule
?,以便它在整個(gè)應(yīng)用程序中生效。
exports: [ RouterModule ]
打開(kāi) ?AppComponent
?的模板,把 ?<app-heroes>
? 元素替換為 ?<router-outlet>
? 元素。
<h1>{{title}}</h1>
<router-outlet></router-outlet>
<app-messages></app-messages>
?AppComponent
?的模板不再需要 ?<app-heroes>
?,因?yàn)橹挥挟?dāng)用戶導(dǎo)航到這里時(shí),才需要顯示 ?HeroesComponent
?。
?<router-outlet>
? 會(huì)告訴路由器要在哪里顯示路由的視圖。
能在 ?AppComponent
?中使用 ?RouterOutlet
?,是因?yàn)?nbsp;?AppModule
?導(dǎo)入了 ?AppRoutingModule
?,而 ?AppRoutingModule
?中導(dǎo)出了 ?RouterModule
?。在本教程開(kāi)始時(shí)你運(yùn)行的那個(gè) ?ng generate
? 命令添加了這個(gè)導(dǎo)入,是因?yàn)?nbsp;?--module=app
? 標(biāo)志。如果你手動(dòng)創(chuàng)建 ?app-routing.module.ts
? 或使用了 CLI 之外的工具,你就要把 ?AppRoutingModule
?導(dǎo)入到 ?app.module.ts
? 中,并且把它添加到 ?NgModule
?的 ?imports
?數(shù)組中
你的 CLI 命令應(yīng)該仍在運(yùn)行吧。
ng serve
瀏覽器應(yīng)該刷新,并顯示著應(yīng)用的標(biāo)題,但是沒(méi)有顯示英雄列表。
看看瀏覽器的地址欄。URL 是以 ?/
? 結(jié)尾的。而到 ?HeroesComponent
?的路由路徑是 ?/heroes
?。
在地址欄中把 ?/heroes
? 追加到 URL 后面。你應(yīng)該能看到熟悉的主從結(jié)構(gòu)的英雄顯示界面。
從瀏覽器地址欄中的 URL 中移除 ?/heroes
?。瀏覽器就會(huì)刷新,并且顯示本應(yīng)用的標(biāo)題,而不顯示英雄列表。
理想情況下,用戶應(yīng)該能通過(guò)點(diǎn)擊鏈接進(jìn)行導(dǎo)航,而不用被迫把路由的 URL 粘貼到地址欄。
添加一個(gè) ?<nav>
? 元素,并在其中放一個(gè)鏈接 ?<a>
? 元素,當(dāng)點(diǎn)擊它時(shí),就會(huì)觸發(fā)一個(gè)到 ?HeroesComponent
?的導(dǎo)航。修改過(guò)的 ?AppComponent
?模板如下:
<h1>{{title}}</h1>
<nav>
<a routerLink="/heroes">Heroes</a>
</nav>
<router-outlet></router-outlet>
<app-messages></app-messages>
routerLink 屬性的值為 ?"/heroes"
?,路由器會(huì)用它來(lái)匹配出指向 ?HeroesComponent
?的路由。 ?routerLink
?是 RouterLink 指令的選擇器,它會(huì)把用戶的點(diǎn)擊轉(zhuǎn)換為路由器的導(dǎo)航操作。 它是 ?RouterModule
?中的另一個(gè)公共指令。
刷新瀏覽器,顯示出了應(yīng)用的標(biāo)題和指向英雄列表的鏈接,但并沒(méi)有顯示英雄列表。
點(diǎn)擊這個(gè)鏈接。地址欄變成了 ?/heroes
?,并且顯示出了英雄列表。
從下面的 最終代碼中把私有 CSS 樣式添加到 ?
app.component.css
? 中,可以讓導(dǎo)航鏈接變得更好看一點(diǎn)。
當(dāng)有多個(gè)視圖時(shí),路由會(huì)更有價(jià)值。不過(guò)目前還只有一個(gè)英雄列表視圖。
使用 CLI 添加一個(gè) ?DashboardComponent
?:
ng generate component dashboard
CLI 生成了 ?DashboardComponent
?的相關(guān)文件,并把它聲明到 ?AppModule
?中。
把這三個(gè)文件中的內(nèi)容改成這樣:
<h2>Top Heroes</h2>
<div class="heroes-menu">
<a *ngFor="let hero of heroes">
{{hero.name}}
</a>
</div>
import { Component, OnInit } from '@angular/core';
import { Hero } from '../hero';
import { HeroService } from '../hero.service';
@Component({
selector: 'app-dashboard',
templateUrl: './dashboard.component.html',
styleUrls: [ './dashboard.component.css' ]
})
export class DashboardComponent implements OnInit {
heroes: Hero[] = [];
constructor(private heroService: HeroService) { }
ngOnInit(): void {
this.getHeroes();
}
getHeroes(): void {
this.heroService.getHeroes()
.subscribe(heroes => this.heroes = heroes.slice(1, 5));
}
}
/* DashboardComponent's private CSS styles */
h2 {
text-align: center;
}
.heroes-menu {
padding: 0;
margin: auto;
max-width: 1000px;
/* flexbox */
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-around;
align-content: flex-start;
align-items: flex-start;
}
a {
background-color: #3f525c;
border-radius: 2px;
padding: 1rem;
font-size: 1.2rem;
text-decoration: none;
display: inline-block;
color: #fff;
text-align: center;
width: 100%;
min-width: 70px;
margin: .5rem auto;
box-sizing: border-box;
/* flexbox */
order: 0;
flex: 0 1 auto;
align-self: auto;
}
@media (min-width: 600px) {
a {
width: 18%;
box-sizing: content-box;
}
}
a:hover {
background-color: #000;
}
這個(gè)模板用來(lái)表示由英雄名字鏈接組成的一個(gè)陣列。
*ngFor
? 復(fù)寫(xiě)器為組件的 ?heroes
?數(shù)組中的每個(gè)條目創(chuàng)建了一個(gè)鏈接。dashboard.component.css
? 中的樣式格式化成了一些色塊。這個(gè)類和 ?HeroesComponent
?類很像。
heroes
?數(shù)組屬性。HeroService
?注入到私有的 ?heroService
?屬性中。ngOnInit()
? 生命周期鉤子中調(diào)用 ?getHeroes()
?。這個(gè) ?getHeroes()
? 函數(shù)會(huì)截取第 2 到 第 5 位英雄,也就是說(shuō)只返回四個(gè)頂層英雄(第二,第三,第四和第五)。
getHeroes(): void {
this.heroService.getHeroes()
.subscribe(heroes => this.heroes = heroes.slice(1, 5));
}
要導(dǎo)航到儀表盤(pán),路由器中就需要一個(gè)相應(yīng)的路由。
把 ?DashboardComponent
?導(dǎo)入到 ?app-routing-module.ts
? 中。
import { DashboardComponent } from './dashboard/dashboard.component';
把一個(gè)指向 ?DashboardComponent
?的路由添加到 ?routes
?數(shù)組中。
{ path: 'dashboard', component: DashboardComponent },
當(dāng)應(yīng)用啟動(dòng)時(shí),瀏覽器的地址欄指向了網(wǎng)站的根路徑。它沒(méi)有匹配到任何現(xiàn)存路由,因此路由器也不會(huì)導(dǎo)航到任何地方。?<router-outlet>
? 下方是空白的。
要讓?xiě)?yīng)用自動(dòng)導(dǎo)航到這個(gè)儀表盤(pán),請(qǐng)把下列路由添加到 ?routes
?數(shù)組中。
{ path: '', redirectTo: '/dashboard', pathMatch: 'full' },
這個(gè)路由會(huì)把一個(gè)與空路徑“完全匹配”的 URL 重定向到路徑為 ?'/dashboard'
? 的路由。
瀏覽器刷新之后,路由器加載了 ?DashboardComponent
?,并且瀏覽器的地址欄會(huì)顯示出 ?/dashboard
? 這個(gè) URL。
應(yīng)該允許用戶通過(guò)點(diǎn)擊頁(yè)面頂部導(dǎo)航區(qū)的各個(gè)鏈接在 ?DashboardComponent
?和 ?HeroesComponent
?之間來(lái)回導(dǎo)航。
把儀表盤(pán)的導(dǎo)航鏈接添加到殼組件 ?AppComponent
?的模板中,就放在 Heroes 鏈接的前面。
<h1>{{title}}</h1>
<nav>
<a routerLink="/dashboard">Dashboard</a>
<a routerLink="/heroes">Heroes</a>
</nav>
<router-outlet></router-outlet>
<app-messages></app-messages>
刷新瀏覽器,你就能通過(guò)點(diǎn)擊這些鏈接在這兩個(gè)視圖之間自由導(dǎo)航了。
?HeroDetailComponent
?可以顯示所選英雄的詳情。此刻,?HeroDetailComponent
?只能在 ?HeroesComponent
?的底部看到。
用戶應(yīng)該能通過(guò)三種途徑看到這些詳情。
在這一節(jié),你將能導(dǎo)航到 ?HeroDetailComponent
?,并把它從 ?HeroesComponent
?中解放出來(lái)。
當(dāng)用戶在 ?HeroesComponent
?中點(diǎn)擊某個(gè)英雄條目時(shí),應(yīng)用應(yīng)該能導(dǎo)航到 ?HeroDetailComponent
?,從英雄列表視圖切換到英雄詳情視圖。英雄列表視圖將不再顯示,而英雄詳情視圖要顯示出來(lái)。
打開(kāi) ?HeroesComponent
?的模板文件(?heroes/heroes.component.html
?),并從底部刪除 ?<app-hero-detail>
? 元素。
目前,點(diǎn)擊某個(gè)英雄條目還沒(méi)有反應(yīng)。不過(guò)當(dāng)你啟用了到 ?HeroDetailComponent
?的路由之后,很快就能修復(fù)它。
要導(dǎo)航到 ?id
?為 ?11
? 的英雄的詳情視圖,類似于 ?~/detail/11
? 的 URL 將是一個(gè)不錯(cuò)的 URL。
打開(kāi) ?app-routing.module.ts
? 并導(dǎo)入 ?HeroDetailComponent
?。
import { HeroDetailComponent } from './hero-detail/hero-detail.component';
然后把一個(gè)參數(shù)化路由添加到 ?routes
?數(shù)組中,它要匹配指向英雄詳情視圖的路徑。
{ path: 'detail/:id', component: HeroDetailComponent },
?path
?中的冒號(hào)(?:
?)表示 ?:id
? 是一個(gè)占位符,它表示某個(gè)特定英雄的 ?id
?。
此刻,應(yīng)用中的所有路由都就緒了。
const routes: Routes = [
{ path: '', redirectTo: '/dashboard', pathMatch: 'full' },
{ path: 'dashboard', component: DashboardComponent },
{ path: 'detail/:id', component: HeroDetailComponent },
{ path: 'heroes', component: HeroesComponent }
];
此刻,?DashboardComponent
?中的英雄連接還沒(méi)有反應(yīng)。
路由器已經(jīng)有一個(gè)指向 ?HeroDetailComponent
?的路由了,修改儀表盤(pán)中的英雄連接,讓它們通過(guò)參數(shù)化的英雄詳情路由進(jìn)行導(dǎo)航。
<a *ngFor="let hero of heroes"
routerLink="/detail/{{hero.id}}">
{{hero.name}}
</a>
你正在 ?*ngFor
? 復(fù)寫(xiě)器中使用 Angular 的插值綁定來(lái)把當(dāng)前迭代的 ?hero.id
? 插入到每個(gè) ?routerLink
?中。
?HeroesComponent
?中的這些英雄條目都是 ?<li>
? 元素,它們的點(diǎn)擊事件都綁定到了組件的 ?onSelect()
? 方法中。
<ul class="heroes">
<li *ngFor="let hero of heroes">
<button type="button" (click)="onSelect(hero)" [class.selected]="hero === selectedHero">
<span class="badge">{{hero.id}}</span>
<span class="name">{{hero.name}}</span>
</button>
</li>
</ul>
清理 ?<li>
?,只保留它的 ?*ngFor
?,把徽章(?<badge>
?)和名字包裹進(jìn)一個(gè) ?<a>
? 元素中, 并且像儀表盤(pán)的模板中那樣為這個(gè) ?<a>
? 元素添加一個(gè) ?routerLink
?屬性。
<ul class="heroes">
<li *ngFor="let hero of heroes">
<a routerLink="/detail/{{hero.id}}">
<span class="badge">{{hero.id}}</span> {{hero.name}}
</a>
</li>
</ul>
你還要修改私有樣式表(?heroes.component.css
?),讓列表恢復(fù)到以前的外觀。
雖然 ?HeroesComponent
?類仍然能正常工作,但 ?onSelect()
? 方法和 ?selectedHero
?屬性已經(jīng)沒(méi)用了。
最好清理掉它們,將來(lái)你會(huì)體會(huì)到這么做的好處。下面是刪除了死代碼之后的類。
export class HeroesComponent implements OnInit {
heroes: Hero[] = [];
constructor(private heroService: HeroService) { }
ngOnInit(): void {
this.getHeroes();
}
getHeroes(): void {
this.heroService.getHeroes()
.subscribe(heroes => this.heroes = heroes);
}
}
以前,父組件 ?HeroesComponent
?會(huì)設(shè)置 ?HeroDetailComponent.hero
? 屬性,然后 ?HeroDetailComponent
?就會(huì)顯示這個(gè)英雄。
?HeroesComponent
?已經(jīng)不會(huì)再那么做了?,F(xiàn)在,當(dāng)路由器會(huì)在響應(yīng)形如 ?~/detail/11
? 的 URL 時(shí)創(chuàng)建 ?HeroDetailComponent
?。
?HeroDetailComponent
?需要從一種新的途徑獲取要顯示的英雄。本節(jié)會(huì)講解如下操作:
id
?HeroService
?從服務(wù)器上獲取具有這個(gè) ?id
?的英雄數(shù)據(jù)。先添加下列導(dǎo)入語(yǔ)句:
import { ActivatedRoute } from '@angular/router';
import { Location } from '@angular/common';
import { HeroService } from '../hero.service';
然后把 ?ActivatedRoute
?、?HeroService
?和 ?Location
?服務(wù)注入到構(gòu)造函數(shù)中,將它們的值保存到私有變量里:
constructor(
private route: ActivatedRoute,
private heroService: HeroService,
private location: Location
) {}
?ActivatedRoute
?保存著到這個(gè) ?HeroDetailComponent
?實(shí)例的路由信息。這個(gè)組件對(duì)從 URL 中提取的路由參數(shù)感興趣。其中的 ?id
?參數(shù)就是要顯示的英雄的 ?id
?。
?HeroService
?從遠(yuǎn)端服務(wù)器獲取英雄數(shù)據(jù),本組件將使用它來(lái)獲取要顯示的英雄。
?location
?是一個(gè) Angular 的服務(wù),用來(lái)與瀏覽器打交道。 稍后,你就會(huì)使用它來(lái)導(dǎo)航回上一個(gè)視圖。
在 ?ngOnInit()
? 生命周期鉤子 中調(diào)用 ?getHero()
?,代碼如下。
ngOnInit(): void {
this.getHero();
}
getHero(): void {
const id = Number(this.route.snapshot.paramMap.get('id'));
this.heroService.getHero(id)
.subscribe(hero => this.hero = hero);
}
?route.snapshot
? 是一個(gè)路由信息的靜態(tài)快照,抓取自組件剛剛創(chuàng)建完畢之后。
?paramMap
?是一個(gè)從 URL 中提取的路由參數(shù)值的字典。?"id"
? 對(duì)應(yīng)的值就是要獲取的英雄的 ?id
?。
路由參數(shù)總會(huì)是字符串。JavaScript 的 ?Number
?函數(shù)會(huì)把字符串轉(zhuǎn)換成數(shù)字,英雄的 ?id
?就是數(shù)字類型。
刷新瀏覽器,應(yīng)用掛了。出現(xiàn)一個(gè)編譯錯(cuò)誤,因?yàn)?nbsp;?HeroService
?沒(méi)有一個(gè)名叫 ?getHero()
? 的方法。這就添加它。
添加 ?HeroService
?,并在 ?getHeroes()
? 后面添加如下的 ?getHero()
? 方法,它接收 ?id
?參數(shù):
getHero(id: number): Observable<Hero> {
// For now, assume that a hero with the specified `id` always exists.
// Error handling will be added in the next step of the tutorial.
const hero = HEROES.find(h => h.id === id)!;
this.messageService.add(`HeroService: fetched hero id=${id}`);
return of(hero);
}
重要:
反引號(hào) ( ?`
? ) 用于定義 JavaScript 的 模板字符串字面量,以便嵌入 ?id
?。
像 ?getHeroes()
? 一樣,?getHero()
? 也有一個(gè)異步函數(shù)簽名。它用 RxJS 的 ?of()
? 函數(shù)返回一個(gè) ?Observable
?形式的模擬英雄數(shù)據(jù)。
你將來(lái)可以用一個(gè)真實(shí)的 ?Http
?請(qǐng)求來(lái)重新實(shí)現(xiàn) ?getHero()
?,而不用修改調(diào)用了它的 ?HeroDetailComponent
?。
刷新瀏覽器,應(yīng)用又恢復(fù)正常了。你可以在儀表盤(pán)或英雄列表中點(diǎn)擊一個(gè)英雄來(lái)導(dǎo)航到該英雄的詳情視圖。
如果你在瀏覽器的地址欄中粘貼了 ?localhost:4200/detail/11
?,路由器也會(huì)導(dǎo)航到 ?id: 11
? 的英雄("Dr. Nice")的詳情視圖。
通過(guò)點(diǎn)擊瀏覽器的后退按鈕,你可以回到英雄列表或儀表盤(pán)視圖,這取決于你從哪里進(jìn)入的詳情視圖。
如果能在 ?HeroDetail
?視圖中也有這么一個(gè)按鈕就更好了。
把一個(gè)后退按鈕添加到組件模板的底部,并且把它綁定到組件的 ?goBack()
? 方法。
<button type="button" (click)="goBack()">go back</button>
在組件類中添加一個(gè) ?goBack()
? 方法,利用你以前注入的 ?Location
?服務(wù)在瀏覽器的歷史棧中后退一步。
goBack(): void {
this.location.back();
}
刷新瀏覽器,并開(kāi)始點(diǎn)擊。用戶能在應(yīng)用中導(dǎo)航:從儀表盤(pán)到英雄詳情再回來(lái),從英雄列表到 mini 版英雄詳情到英雄詳情,再回到英雄列表。
當(dāng)你將一些私有 CSS 樣式添加到 ?hero-detail.component.css
? 里之后,其細(xì)節(jié)看起來(lái)會(huì)更好,如下面的“查看最終代碼”標(biāo)簽頁(yè)中所示。
下面是本頁(yè)所提到的源代碼。
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { DashboardComponent } from './dashboard/dashboard.component';
import { HeroDetailComponent } from './hero-detail/hero-detail.component';
import { HeroesComponent } from './heroes/heroes.component';
import { MessagesComponent } from './messages/messages.component';
import { AppRoutingModule } from './app-routing.module';
@NgModule({
imports: [
BrowserModule,
FormsModule,
AppRoutingModule
],
declarations: [
AppComponent,
DashboardComponent,
HeroesComponent,
HeroDetailComponent,
MessagesComponent
],
bootstrap: [ AppComponent ]
})
export class AppModule { }
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { DashboardComponent } from './dashboard/dashboard.component';
import { HeroesComponent } from './heroes/heroes.component';
import { HeroDetailComponent } from './hero-detail/hero-detail.component';
const routes: Routes = [
{ path: '', redirectTo: '/dashboard', pathMatch: 'full' },
{ path: 'dashboard', component: DashboardComponent },
{ path: 'detail/:id', component: HeroDetailComponent },
{ path: 'heroes', component: HeroesComponent }
];
@NgModule({
imports: [ RouterModule.forRoot(routes) ],
exports: [ RouterModule ]
})
export class AppRoutingModule {}
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { Hero } from './hero';
import { HEROES } from './mock-heroes';
import { MessageService } from './message.service';
@Injectable({ providedIn: 'root' })
export class HeroService {
constructor(private messageService: MessageService) { }
getHeroes(): Observable<Hero[]> {
const heroes = of(HEROES);
this.messageService.add('HeroService: fetched heroes');
return heroes;
}
getHero(id: number): Observable<Hero> {
// For now, assume that a hero with the specified `id` always exists.
// Error handling will be added in the next step of the tutorial.
const hero = HEROES.find(h => h.id === id)!;
this.messageService.add(`HeroService: fetched hero id=${id}`);
return of(hero);
}
}
<h1>{{title}}</h1>
<nav>
<a routerLink="/dashboard">Dashboard</a>
<a routerLink="/heroes">Heroes</a>
</nav>
<router-outlet></router-outlet>
<app-messages></app-messages>
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'Tour of Heroes';
}
/* AppComponent's private CSS styles */
h1 {
margin-bottom: 0;
}
nav a {
padding: 1rem;
text-decoration: none;
margin-top: 10px;
display: inline-block;
background-color: #e8e8e8;
color: #3d3d3d;
border-radius: 4px;
}
nav a:hover {
color: white;
background-color: #42545C;
}
nav a.active {
background-color: black;
}
<h2>Top Heroes</h2>
<div class="heroes-menu">
<a *ngFor="let hero of heroes"
routerLink="/detail/{{hero.id}}">
{{hero.name}}
</a>
</div>
import { Component, OnInit } from '@angular/core';
import { Hero } from '../hero';
import { HeroService } from '../hero.service';
@Component({
selector: 'app-dashboard',
templateUrl: './dashboard.component.html',
styleUrls: [ './dashboard.component.css' ]
})
export class DashboardComponent implements OnInit {
heroes: Hero[] = [];
constructor(private heroService: HeroService) { }
ngOnInit(): void {
this.getHeroes();
}
getHeroes(): void {
this.heroService.getHeroes()
.subscribe(heroes => this.heroes = heroes.slice(1, 5));
}
}
/* DashboardComponent's private CSS styles */
h2 {
text-align: center;
}
.heroes-menu {
padding: 0;
margin: auto;
max-width: 1000px;
/* flexbox */
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-around;
align-content: flex-start;
align-items: flex-start;
}
a {
background-color: #3f525c;
border-radius: 2px;
padding: 1rem;
font-size: 1.2rem;
text-decoration: none;
display: inline-block;
color: #fff;
text-align: center;
width: 100%;
min-width: 70px;
margin: .5rem auto;
box-sizing: border-box;
/* flexbox */
order: 0;
flex: 0 1 auto;
align-self: auto;
}
@media (min-width: 600px) {
a {
width: 18%;
box-sizing: content-box;
}
}
a:hover {
background-color: #000;
}
<h2>My Heroes</h2>
<ul class="heroes">
<li *ngFor="let hero of heroes">
<a routerLink="/detail/{{hero.id}}">
<span class="badge">{{hero.id}}</span> {{hero.name}}
</a>
</li>
</ul>
import { Component, OnInit } from '@angular/core';
import { Hero } from '../hero';
import { HeroService } from '../hero.service';
@Component({
selector: 'app-heroes',
templateUrl: './heroes.component.html',
styleUrls: ['./heroes.component.css']
})
export class HeroesComponent implements OnInit {
heroes: Hero[] = [];
constructor(private heroService: HeroService) { }
ngOnInit(): void {
this.getHeroes();
}
getHeroes(): void {
this.heroService.getHeroes()
.subscribe(heroes => this.heroes = heroes);
}
}
/* HeroesComponent's private CSS styles */
.heroes {
margin: 0 0 2em 0;
list-style-type: none;
padding: 0;
width: 15em;
}
.heroes li {
position: relative;
cursor: pointer;
}
.heroes li:hover {
left: .1em;
}
.heroes a {
color: #333;
text-decoration: none;
background-color: #EEE;
margin: .5em;
padding: .3em 0;
height: 1.6em;
border-radius: 4px;
display: block;
width: 100%;
}
.heroes a:hover {
color: #2c3a41;
background-color: #e6e6e6;
}
.heroes a:active {
background-color: #525252;
color: #fafafa;
}
.heroes .badge {
display: inline-block;
font-size: small;
color: white;
padding: 0.8em 0.7em 0 0.7em;
background-color: #405061;
line-height: 1em;
position: relative;
left: -1px;
top: -4px;
height: 1.8em;
min-width: 16px;
text-align: right;
margin-right: .8em;
border-radius: 4px 0 0 4px;
}
<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="Hero name"/>
</div>
<button type="button" (click)="goBack()">go back</button>
</div>
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Location } from '@angular/common';
import { Hero } from '../hero';
import { HeroService } from '../hero.service';
@Component({
selector: 'app-hero-detail',
templateUrl: './hero-detail.component.html',
styleUrls: [ './hero-detail.component.css' ]
})
export class HeroDetailComponent implements OnInit {
hero: Hero | undefined;
constructor(
private route: ActivatedRoute,
private heroService: HeroService,
private location: Location
) {}
ngOnInit(): void {
this.getHero();
}
getHero(): void {
const id = Number(this.route.snapshot.paramMap.get('id'));
this.heroService.getHero(id)
.subscribe(hero => this.hero = hero);
}
goBack(): void {
this.location.back();
}
}
/* HeroDetailComponent's private CSS styles */
label {
color: #435960;
font-weight: bold;
}
input {
font-size: 1em;
padding: .5rem;
}
button {
margin-top: 20px;
background-color: #eee;
padding: 1rem;
border-radius: 4px;
font-size: 1rem;
}
button:hover {
background-color: #cfd8dc;
}
button:disabled {
background-color: #eee;
color: #ccc;
cursor: auto;
}
<a>
? 鏈接和一個(gè) ?<router-outlet>
? 把 ?AppComponent
?轉(zhuǎn)換成了一個(gè)導(dǎo)航用的殼組件AppRoutingModule
?中配置了路由器<a>
? 元素中使用了 ?routerLink
?指令HeroService
?服務(wù)
更多建議: