有一些《英雄之旅》的新需求:
要查看本頁所講的范例程序,參閱現(xiàn)場演練 / 下載范例。
完成時,用戶就能像這樣在應(yīng)用中導(dǎo)航:
在 Angular 中,最好在一個獨立的頂層模塊中加載和配置路由器,它專注于路由功能,然后由根模塊 ?AppModule
?導(dǎo)入它。
按照慣例,這個模塊類的名字叫做 ?AppRoutingModule
?,并且位于 ?src/app
? 下的 ?app-routing.module.ts
? 文件中。
使用 CLI 生成它。
ng generate module app-routing --flat --module=app
參數(shù) |
詳情 |
---|---|
--flat
|
把這個文件放進(jìn)了 |
--module=app
|
告訴 CLI 把它注冊到 |
生成的文件是這樣的:
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
? 會導(dǎo)入 ?RouterModule
?和 ?Routes
?,以便該應(yīng)用具有路由功能。配置好路由后,接著導(dǎo)入 ?HeroesComponent
?,它將告訴路由器要去什么地方。
注意,對 ?CommonModule
?的引用和 ?declarations
?數(shù)組不是必要的,因此它們不再是 ?AppRoutingModule
?的一部分。以下各節(jié)將詳細(xì)介紹 ?AppRoutingModule
?的其余部分。
該文件的下一部分是你的路由配置。Routes 告訴路由器,當(dāng)用戶單擊鏈接或?qū)?nbsp;URL 粘貼進(jìn)瀏覽器地址欄時要顯示哪個視圖。
由于 ?app-routing.module.ts
? 已經(jīng)導(dǎo)入了 ?HeroesComponent
?,因此你可以直接在 ?routes
?數(shù)組中使用它:
const routes: Routes = [
{ path: 'heroes', component: HeroesComponent }
];
典型的 Angular ?Route
?具有兩個屬性:
屬性 |
詳情 |
---|---|
path
|
用來匹配瀏覽器地址欄中 URL 的字符串。 |
component
|
導(dǎo)航到該路由時,路由器應(yīng)該創(chuàng)建的組件。 |
這會告訴路由器把該 URL 與 ?path:'heroes'
? 匹配。如果網(wǎng)址類似于 ?localhost:4200/heroes
? 就顯示 ?HeroesComponent
?。
?@NgModule
? 元數(shù)據(jù)會初始化路由器,并開始監(jiān)聽瀏覽器地址的變化。
下面的代碼行將 ?RouterModule
?添加到 ?AppRoutingModule
?的 ?imports
?數(shù)組中,同時通過調(diào)用 ?RouterModule.forRoot()
? 來用這些 ?routes
?配置它:
imports: [ RouterModule.forRoot(routes) ],
這個方法之所以叫 ?
forRoot()
?,是因為你要在應(yīng)用的頂層配置這個路由器。?forRoot()
? 方法會提供路由所需的服務(wù)提供者和指令,還會基于瀏覽器的當(dāng)前 URL 執(zhí)行首次導(dǎo)航。
接下來,?AppRoutingModule
?導(dǎo)出 ?RouterModule
?,以便它在整個應(yīng)用程序中生效。
exports: [ RouterModule ]
打開 ?AppComponent
?的模板,把 ?<app-heroes>
? 元素替換為 ?<router-outlet>
? 元素。
<h1>{{title}}</h1>
<router-outlet></router-outlet>
<app-messages></app-messages>
?AppComponent
?的模板不再需要 ?<app-heroes>
?,因為只有當(dāng)用戶導(dǎo)航到這里時,才需要顯示 ?HeroesComponent
?。
?<router-outlet>
? 會告訴路由器要在哪里顯示路由的視圖。
能在 ?AppComponent
?中使用 ?RouterOutlet
?,是因為 ?AppModule
?導(dǎo)入了 ?AppRoutingModule
?,而 ?AppRoutingModule
?中導(dǎo)出了 ?RouterModule
?。在本教程開始時你運(yùn)行的那個 ?ng generate
? 命令添加了這個導(dǎo)入,是因為 ?--module=app
? 標(biāo)志。如果你手動創(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)題,但是沒有顯示英雄列表。
看看瀏覽器的地址欄。URL 是以 ?/
? 結(jié)尾的。而到 ?HeroesComponent
?的路由路徑是 ?/heroes
?。
在地址欄中把 ?/heroes
? 追加到 URL 后面。你應(yīng)該能看到熟悉的主從結(jié)構(gòu)的英雄顯示界面。
從瀏覽器地址欄中的 URL 中移除 ?/heroes
?。瀏覽器就會刷新,并且顯示本應(yīng)用的標(biāo)題,而不顯示英雄列表。
理想情況下,用戶應(yīng)該能通過點擊鏈接進(jìn)行導(dǎo)航,而不用被迫把路由的 URL 粘貼到地址欄。
添加一個 ?<nav>
? 元素,并在其中放一個鏈接 ?<a>
? 元素,當(dāng)點擊它時,就會觸發(fā)一個到 ?HeroesComponent
?的導(dǎo)航。修改過的 ?AppComponent
?模板如下:
<h1>{{title}}</h1>
<nav>
<a routerLink="/heroes">Heroes</a>
</nav>
<router-outlet></router-outlet>
<app-messages></app-messages>
routerLink 屬性的值為 ?"/heroes"
?,路由器會用它來匹配出指向 ?HeroesComponent
?的路由。 ?routerLink
?是 RouterLink 指令的選擇器,它會把用戶的點擊轉(zhuǎn)換為路由器的導(dǎo)航操作。 它是 ?RouterModule
?中的另一個公共指令。
刷新瀏覽器,顯示出了應(yīng)用的標(biāo)題和指向英雄列表的鏈接,但并沒有顯示英雄列表。
點擊這個鏈接。地址欄變成了 ?/heroes
?,并且顯示出了英雄列表。
從下面的 最終代碼中把私有 CSS 樣式添加到 ?
app.component.css
? 中,可以讓導(dǎo)航鏈接變得更好看一點。
當(dāng)有多個視圖時,路由會更有價值。不過目前還只有一個英雄列表視圖。
使用 CLI 添加一個 ?DashboardComponent
?:
ng generate component dashboard
CLI 生成了 ?DashboardComponent
?的相關(guān)文件,并把它聲明到 ?AppModule
?中。
把這三個文件中的內(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;
}
這個模板用來表示由英雄名字鏈接組成的一個陣列。
*ngFor
? 復(fù)寫器為組件的 ?heroes
?數(shù)組中的每個條目創(chuàng)建了一個鏈接。dashboard.component.css
? 中的樣式格式化成了一些色塊。這個類和 ?HeroesComponent
?類很像。
heroes
?數(shù)組屬性。HeroService
?注入到私有的 ?heroService
?屬性中。ngOnInit()
? 生命周期鉤子中調(diào)用 ?getHeroes()
?。這個 ?getHeroes()
? 函數(shù)會截取第 2 到 第 5 位英雄,也就是說只返回四個頂層英雄(第二,第三,第四和第五)。
getHeroes(): void {
this.heroService.getHeroes()
.subscribe(heroes => this.heroes = heroes.slice(1, 5));
}
要導(dǎo)航到儀表盤,路由器中就需要一個相應(yīng)的路由。
把 ?DashboardComponent
?導(dǎo)入到 ?app-routing-module.ts
? 中。
import { DashboardComponent } from './dashboard/dashboard.component';
把一個指向 ?DashboardComponent
?的路由添加到 ?routes
?數(shù)組中。
{ path: 'dashboard', component: DashboardComponent },
當(dāng)應(yīng)用啟動時,瀏覽器的地址欄指向了網(wǎng)站的根路徑。它沒有匹配到任何現(xiàn)存路由,因此路由器也不會導(dǎo)航到任何地方。?<router-outlet>
? 下方是空白的。
要讓應(yīng)用自動導(dǎo)航到這個儀表盤,請把下列路由添加到 ?routes
?數(shù)組中。
{ path: '', redirectTo: '/dashboard', pathMatch: 'full' },
這個路由會把一個與空路徑“完全匹配”的 URL 重定向到路徑為 ?'/dashboard'
? 的路由。
瀏覽器刷新之后,路由器加載了 ?DashboardComponent
?,并且瀏覽器的地址欄會顯示出 ?/dashboard
? 這個 URL。
應(yīng)該允許用戶通過點擊頁面頂部導(dǎo)航區(qū)的各個鏈接在 ?DashboardComponent
?和 ?HeroesComponent
?之間來回導(dǎo)航。
把儀表盤的導(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>
刷新瀏覽器,你就能通過點擊這些鏈接在這兩個視圖之間自由導(dǎo)航了。
?HeroDetailComponent
?可以顯示所選英雄的詳情。此刻,?HeroDetailComponent
?只能在 ?HeroesComponent
?的底部看到。
用戶應(yīng)該能通過三種途徑看到這些詳情。
在這一節(jié),你將能導(dǎo)航到 ?HeroDetailComponent
?,并把它從 ?HeroesComponent
?中解放出來。
當(dāng)用戶在 ?HeroesComponent
?中點擊某個英雄條目時,應(yīng)用應(yīng)該能導(dǎo)航到 ?HeroDetailComponent
?,從英雄列表視圖切換到英雄詳情視圖。英雄列表視圖將不再顯示,而英雄詳情視圖要顯示出來。
打開 ?HeroesComponent
?的模板文件(?heroes/heroes.component.html
?),并從底部刪除 ?<app-hero-detail>
? 元素。
目前,點擊某個英雄條目還沒有反應(yīng)。不過當(dāng)你啟用了到 ?HeroDetailComponent
?的路由之后,很快就能修復(fù)它。
要導(dǎo)航到 ?id
?為 ?11
? 的英雄的詳情視圖,類似于 ?~/detail/11
? 的 URL 將是一個不錯的 URL。
打開 ?app-routing.module.ts
? 并導(dǎo)入 ?HeroDetailComponent
?。
import { HeroDetailComponent } from './hero-detail/hero-detail.component';
然后把一個參數(shù)化路由添加到 ?routes
?數(shù)組中,它要匹配指向英雄詳情視圖的路徑。
{ path: 'detail/:id', component: HeroDetailComponent },
?path
?中的冒號(?:
?)表示 ?:id
? 是一個占位符,它表示某個特定英雄的 ?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
?中的英雄連接還沒有反應(yīng)。
路由器已經(jīng)有一個指向 ?HeroDetailComponent
?的路由了,修改儀表盤中的英雄連接,讓它們通過參數(shù)化的英雄詳情路由進(jìn)行導(dǎo)航。
<a *ngFor="let hero of heroes"
routerLink="/detail/{{hero.id}}">
{{hero.name}}
</a>
你正在 ?*ngFor
? 復(fù)寫器中使用 Angular 的插值綁定來把當(dāng)前迭代的 ?hero.id
? 插入到每個 ?routerLink
?中。
?HeroesComponent
?中的這些英雄條目都是 ?<li>
? 元素,它們的點擊事件都綁定到了組件的 ?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)一個 ?<a>
? 元素中, 并且像儀表盤的模板中那樣為這個 ?<a>
? 元素添加一個 ?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)沒用了。
最好清理掉它們,將來你會體會到這么做的好處。下面是刪除了死代碼之后的類。
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
?會設(shè)置 ?HeroDetailComponent.hero
? 屬性,然后 ?HeroDetailComponent
?就會顯示這個英雄。
?HeroesComponent
?已經(jīng)不會再那么做了。現(xiàn)在,當(dāng)路由器會在響應(yīng)形如 ?~/detail/11
? 的 URL 時創(chuàng)建 ?HeroDetailComponent
?。
?HeroDetailComponent
?需要從一種新的途徑獲取要顯示的英雄。本節(jié)會講解如下操作:
id
?HeroService
?從服務(wù)器上獲取具有這個 ?id
?的英雄數(shù)據(jù)。先添加下列導(dǎo)入語句:
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
?保存著到這個 ?HeroDetailComponent
?實例的路由信息。這個組件對從 URL 中提取的路由參數(shù)感興趣。其中的 ?id
?參數(shù)就是要顯示的英雄的 ?id
?。
?HeroService
?從遠(yuǎn)端服務(wù)器獲取英雄數(shù)據(jù),本組件將使用它來獲取要顯示的英雄。
?location
?是一個 Angular 的服務(wù),用來與瀏覽器打交道。 稍后,你就會使用它來導(dǎo)航回上一個視圖。
在 ?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
? 是一個路由信息的靜態(tài)快照,抓取自組件剛剛創(chuàng)建完畢之后。
?paramMap
?是一個從 URL 中提取的路由參數(shù)值的字典。?"id"
? 對應(yīng)的值就是要獲取的英雄的 ?id
?。
路由參數(shù)總會是字符串。JavaScript 的 ?Number
?函數(shù)會把字符串轉(zhuǎn)換成數(shù)字,英雄的 ?id
?就是數(shù)字類型。
刷新瀏覽器,應(yīng)用掛了。出現(xiàn)一個編譯錯誤,因為 ?HeroService
?沒有一個名叫 ?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);
}
重要:
反引號 ( ?`
? ) 用于定義 JavaScript 的 模板字符串字面量,以便嵌入 ?id
?。
像 ?getHeroes()
? 一樣,?getHero()
? 也有一個異步函數(shù)簽名。它用 RxJS 的 ?of()
? 函數(shù)返回一個 ?Observable
?形式的模擬英雄數(shù)據(jù)。
你將來可以用一個真實的 ?Http
?請求來重新實現(xiàn) ?getHero()
?,而不用修改調(diào)用了它的 ?HeroDetailComponent
?。
刷新瀏覽器,應(yīng)用又恢復(fù)正常了。你可以在儀表盤或英雄列表中點擊一個英雄來導(dǎo)航到該英雄的詳情視圖。
如果你在瀏覽器的地址欄中粘貼了 ?localhost:4200/detail/11
?,路由器也會導(dǎo)航到 ?id: 11
? 的英雄("Dr. Nice")的詳情視圖。
通過點擊瀏覽器的后退按鈕,你可以回到英雄列表或儀表盤視圖,這取決于你從哪里進(jìn)入的詳情視圖。
如果能在 ?HeroDetail
?視圖中也有這么一個按鈕就更好了。
把一個后退按鈕添加到組件模板的底部,并且把它綁定到組件的 ?goBack()
? 方法。
<button type="button" (click)="goBack()">go back</button>
在組件類中添加一個 ?goBack()
? 方法,利用你以前注入的 ?Location
?服務(wù)在瀏覽器的歷史棧中后退一步。
goBack(): void {
this.location.back();
}
刷新瀏覽器,并開始點擊。用戶能在應(yīng)用中導(dǎo)航:從儀表盤到英雄詳情再回來,從英雄列表到 mini 版英雄詳情到英雄詳情,再回到英雄列表。
當(dāng)你將一些私有 CSS 樣式添加到 ?hero-detail.component.css
? 里之后,其細(xì)節(jié)看起來會更好,如下面的“查看最終代碼”標(biāo)簽頁中所示。
下面是本頁所提到的源代碼。
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>
? 鏈接和一個 ?<router-outlet>
? 把 ?AppComponent
?轉(zhuǎn)換成了一個導(dǎo)航用的殼組件AppRoutingModule
?中配置了路由器<a>
? 元素中使用了 ?routerLink
?指令HeroService
?服務(wù)
更多建議: