版本:4.0.0+2
有一些英雄指南應(yīng)用的新需求:
- 添加一個儀表盤 視圖咸作。
- 添加在英雄 視圖和 儀表盤 視圖之間導(dǎo)航的功能智末。
- 用戶無論在哪個視圖中點擊一個英雄采转,都會導(dǎo)航到所選英雄的詳情視圖璧亮。
- 用戶在郵件中點擊一個深鏈接乖订,會打開一個特定英雄的詳情視圖终息。
完成時夺巩,用戶就能像這樣在應(yīng)用中導(dǎo)航:
把 Angular 路由加入到應(yīng)用中贞让,以滿足這些需求。
更多關(guān)于路由器的信息柳譬,請看 Routing and Navigation喳张。
當(dāng)完成本章的學(xué)習(xí),應(yīng)用看起來這樣——在線示例 (查看源碼)美澳。
我們離開的地方
在繼續(xù)英雄指南之前销部,檢查你是否有如下結(jié)構(gòu)。
如果應(yīng)用不運行了制跟,啟動應(yīng)用舅桩。當(dāng)你做出修改時,通過刷新瀏覽器保持繼續(xù)運行雨膨。
行動計劃
下面是我們的計劃:
- 把
AppComponent
變成應(yīng)用程序的“殼”擂涛,它只處理導(dǎo)航。 - 把現(xiàn)在由
AppComponent
關(guān)注的英雄移到一個獨立的HeroesComponent
中聊记。 - 添加路由歼指。
- 創(chuàng)建一個新的
DashboardComponent
組件。 - 把 儀表盤 加入導(dǎo)航結(jié)構(gòu)中甥雕。
路由 是 導(dǎo)航 的另一個名字踩身。路由器 是從一個視圖導(dǎo)航到另一個視圖的機制。
拆分 AppComponent
現(xiàn)在的應(yīng)用載入AppComponent
后立刻顯示出英雄列表社露。修改后的應(yīng)用應(yīng)該呈現(xiàn)一個選擇視圖(儀表盤和英雄)的殼挟阻,然后默認(rèn)顯示其中之一。
AppComponent
組件應(yīng)該只處理導(dǎo)航峭弟,所以你要把英雄列表的顯示附鸽,從AppComponent
移到它自己的HeroesComponent
組件中。
HeroesComponent
AppComponent
現(xiàn)在的功能已經(jīng)專注于英雄數(shù)據(jù)了瞒瘸。與其把AppComponent
中所有的東西都移出去坷备,不如索性把它改名為HeroesComponent
,然后創(chuàng)建一個單獨的AppComponent
殼情臭。
按如下來做:
- 重命名并移動
app_component.*
文件到src/heroes_component.*
省撑。 - 移除導(dǎo)入路徑中的
src/
前綴。 - 重命名
AppComponent
類為HeroesComponent
(本地重命名俯在,只在這個文件中)竟秫。 - 重命名選擇器
my-app
為my-heroes
。 - 改變模板 URL 為
heroes_component.html
跷乐,以及樣式文件heroes_component.css
肥败。
// lib/src/heroes_component.dart (showing renamings only)
@Component(
selector: 'my-heroes',
templateUrl: 'heroes_component.html',
styleUrls: const ['heroes_component.css'],
)
class HeroesComponent implements OnInit {
HeroesComponent(
this._heroService,
);
}
創(chuàng)建 AppComponent
新的AppComponent
是應(yīng)用的“殼”。它將在頂部放一些導(dǎo)航鏈接,并在下面有個顯示區(qū)域馒稍。
執(zhí)行這些步驟:
- 創(chuàng)建
lib/app_component.dart
文件皿哨。 - 定義一個
AppComponent
類。 - 在類的上方添加一個帶有
my-app
選擇器的@Component
注解纽谒。 - 從 heroes 組件中移動下面的東西到
AppComponent
:-
title
類屬性往史。 -
@Component
的模板中包含了title
綁定的<h1>
元素。
-
- 在應(yīng)用模板緊跟標(biāo)題的下面添加
<my-heroes>
元素佛舱,以便你仍然能看到英雄椎例。 - 將
HeroesComponent
添加到AppComponent
的directives
列表中,以便 Angular 能夠識別<my-heroes>
標(biāo)簽请祖。 - 將
HeroService
添加到AppComponent
的providers
列表中订歪,因為在其它每個視圖中你都需要它。 - 從
HerosComponent
的providers
列表中移除HeroService
肆捕,因為它已經(jīng)被提升到AppComponent
了刷晋。 - 為
AppComponent
添加import
語句。
第一稿看起來這樣:
// lib/app_component.dart
import 'package:angular/angular.dart';
import 'src/hero_service.dart';
import 'src/heroes_component.dart';
@Component(
selector: 'my-app',
template: '''
<h1>{{title}}</h1>
<my-heroes></my-heroes>
''',
directives: const [HeroesComponent],
providers: const [HeroService],
)
class AppComponent {
final title = 'Tour of Heroes';
}
刷新瀏覽器慎陵。應(yīng)用仍然運行眼虱,并顯示英雄列表。
添加路由
英雄列表應(yīng)該在用戶點擊按鈕之后顯示席纽,而不是自動顯示捏悬。換句話說,用戶應(yīng)該能夠?qū)Ш降接⑿哿斜怼?/p>
更新 pubspec 文件
使用 Angular 路由(angular_router)使導(dǎo)航成為可能润梯。由于路由在它自己的包里过牙,首先添加包到應(yīng)用的 pubspec 文件:
// {toh-4 → toh-5}/pubspec.yaml
dependencies:
angular: ^4.0.0
angular_forms: ^1.0.0
+ angular_router: ^1.0.2
不是所有的應(yīng)用都需要路由,這就是為什么 Angular 路由是在一個獨立的纺铭、可選的包中寇钉。
導(dǎo)入庫
Angular 路由是多個服務(wù) (ROUTER_PROVIDERS)
、指令(ROUTER_DIRECTIVES)
和配置類的組合舶赔。通過導(dǎo)入路由庫來獲取它們:
// lib/app_component.dart (router import)
import 'package:angular_router/angular_router.dart';
使路由可用
在應(yīng)用的 bootstrap 函數(shù)中指定 ROUTER_PROVIDERS 來告訴 Angular 你的應(yīng)用使用了路由扫倡。
// web/main.dart
import 'package:angular/angular.dart';
import 'package:angular_router/angular_router.dart';
import 'package:angular_tour_of_heroes/app_component.dart';
void main() {
bootstrap(AppComponent, [
ROUTER_PROVIDERS,
// Remove next line in production
provide(LocationStrategy, useClass: HashLocationStrategy),
]);
}
使用哪個位置策略
默認(rèn)的 LocationStrategy 是 PathLocationStrategy,所以在生產(chǎn)環(huán)境中竟纳,你可以使用沒有 LocationStrategy 提供器覆寫的ROUTER_PROVIDERS撵溃。在開發(fā)過程中,由于
pub serve
不支持深鏈接蚁袭,使用 HashLocationStrategy 更方便征懈。詳細(xì)信息請看附錄:位置策略和瀏覽器 URL 模式。
接下來揩悄,添加 ROUTER_DIRECTIVES 到@Component
注解,并移除HeroesComponent
:
// lib/app_component.dart (directives)
directives: const [ROUTER_DIRECTIVES],
由于AppComponent
沒有直接顯示英雄鬼悠,那是路由的工作删性,你可以從指令列表中移除HeroesComponent
亏娜。很快你將從模板中移除<my-heroes>
。
<base href>
打開index.html
并確保在<head>
部分的頂部有一個<base href="...">
元素(或一個動態(tài)設(shè)置這個元素的 script 標(biāo)簽)蹬挺。
正如在路由和導(dǎo)航章節(jié)的 Set the base href 部分所述维贺,示例應(yīng)用使用了下面的腳本:
// web/index.html (base-href)
<head>
<script>
// WARNING: DO NOT set the <base href> like this in production!
// Details: https://webdev.dartlang.org/angular/guide/router
(function () {
var m = document.location.pathname.match(/^(\/[-\w]+)+\/web($|\/)/);
document.write('<base href="' + (m ? m[0] : '/') + '" />');
}());
</script>
配置路由
當(dāng)用戶點擊鏈接或者把 URL 粘貼到瀏覽器地址欄時,路由定義 告訴路由器應(yīng)該顯示哪個視圖巴帮。
創(chuàng)建一個路由配置(RouteConfig)來保存應(yīng)用路由定義 的列表溯泣。定義第一個路由作為 heroes 組件的路由:
// lib/app_component.dart (Heroes route)
@RouteConfig(const [
const Route(path: '/heroes', name: 'Heroes', component: HeroesComponent)
])
路由定義是一個包含如下命名參數(shù)的 Route 對象:
-
path
:路由器把這個字符串和瀏覽器地址欄中的URL(/heroes
)進行匹配。 -
name
:路由名(Heroes
)榕茧。必須以大寫字母開頭垃沦,避免與路徑相混淆。 -
component
:當(dāng)路由被導(dǎo)航到(HeroesComponent
)時組件將會被激活用押。
更多關(guān)于路由定義的內(nèi)容請看——路由和導(dǎo)航肢簿。
路由插座
如果你訪問 localhost:8080/#/heroes,路由器會把 URL 匹配到 heroes 路由蜻拨,并顯示HeroesComponent
池充。然而,你必須告訴路由器在哪里顯示這個組件缎讼。
為此收夸,在模板的底部添加一個<router-outlet>
元素。 RouterOutlet是ROUTER_DIRECTIVES中的一個血崭。當(dāng)用戶在應(yīng)用中導(dǎo)航時咱圆,路由器會立刻在<router-outlet>
下面顯示每個組件。
刷新瀏覽器功氨,然后訪問 localhost:8080/#/heroes序苏。應(yīng)該能看到英雄列表。
路由器鏈接
用戶不應(yīng)該往地址欄中粘貼一個路由地址捷凄,而應(yīng)該在模板中添加一個錨標(biāo)簽忱详。當(dāng)點擊時,觸發(fā)到HeroesComponent
的導(dǎo)航跺涤。
修改過的模板看起來是這樣的:
// lib/app_component.dart (template)
template: '''
<h1>{{title}}</h1>
<a [routerLink]="['Heroes']">Heroes</a>
<router-outlet></router-outlet>
''',
注意匈睁,錨點標(biāo)簽中的[routerLink]
綁定。當(dāng)用戶點擊這個鏈接時桶错, RouterLink 指令告訴路由器應(yīng)該導(dǎo)航到哪里航唆。
你定義了一個帶鏈接參數(shù)列表的路由指示。在我們的小例子中院刁,列表只有一個元素糯钙,用引號引起來的路由的 name 。回去查看路由配置任岸,證實'Heroes'
是HeroesComponent
路由的 name再榄。
更多關(guān)于鏈接參數(shù)列表內(nèi)容請看路由章節(jié)。
刷新瀏覽器享潜。瀏覽器顯示了應(yīng)用標(biāo)題和英雄鏈接困鸥,沒有英雄列表。點擊 Heroes 導(dǎo)航鏈接剑按。地址欄變成/#/heroes
(或是等價的/#heroes
)疾就,并且英雄列表顯示出來了。
AppComponent
現(xiàn)在看起來這樣:
// lib/app_component.dart
import 'package:angular/angular.dart';
import 'package:angular_router/angular_router.dart';
import 'src/hero_service.dart';
import 'src/heroes_component.dart';
@Component(
selector: 'my-app',
template: '''
<h1>{{title}}</h1>
<a [routerLink]="['Heroes']">Heroes</a>
<router-outlet></router-outlet>
''',
directives: const [ROUTER_DIRECTIVES],
providers: const [HeroService],
)
@RouteConfig(const [
const Route(path: '/heroes', name: 'Heroes', component: HeroesComponent)
])
class AppComponent {
final title = 'Tour of Heroes';
}
AppComponent 有一個路由器艺蝴,并且顯示路由視圖猬腰。因此,為了和其它類型的組件區(qū)分吴趴,這種組件類型稱為路由器組件漆诽。
添加一個儀表盤
只有當(dāng)多個視圖存在的時候,路由才有意義锣枝。為了添加其它視圖厢拭,先創(chuàng)建DashboardComponent
占個位置。
// lib/src/dashboard_component.dart (v1)
import 'package:angular/angular.dart';
@Component(
selector: 'my-dashboard',
template: '<h3>My Dashboard</h3>',
)
class DashboardComponent {}
稍后你將會使這個組件更有用撇叁。
配置儀表盤路由
添加一個和英雄路由相似的儀表盤路由:
// lib/app_component.dart (Dashboard route)
const Route(
path: '/dashboard',
name: 'Dashboard',
component: DashboardComponent,
),
添加重定向路由
當(dāng)前供鸠,瀏覽器啟動時地址欄是/
。當(dāng)應(yīng)用開始時陨闹,它應(yīng)該顯示儀表盤楞捂,并在地址欄中顯示路徑/#/dashboard
。
添加一個重定向路由來實現(xiàn)這一點:
// lib/app_component.dart (Redirect route)
const Redirect(path: '/', redirectTo: const ['Dashboard']),
或者趋厉,你可以定義
Dashboard
為默認(rèn)路由寨闹。更多關(guān)于默認(rèn)路由和重定向的內(nèi)容請看路由與導(dǎo)航章節(jié)。
添加導(dǎo)航到儀表盤
在模板中添加一個儀表盤導(dǎo)航鏈接君账,就放在Heroes
鏈接的上方繁堡。
// lib/app_component.dart (template)
template: '''
<h1>{{title}}</h1>
<nav>
<a [routerLink]="['Dashboard']">Dashboard</a>
<a [routerLink]="['Heroes']">Heroes</a>
</nav>
<router-outlet></router-outlet>
''',
<nav>
標(biāo)簽現(xiàn)在什么也不做,但稍后乡数,當(dāng)你對這些鏈接添加樣式時椭蹄,它們會很有用。
在瀏覽器中打開净赴,進入到應(yīng)用的根(/
)路徑并重新加載绳矩。應(yīng)用顯示了儀表盤,并且你可以在儀表盤和英雄列表之間導(dǎo)航玖翅。
給儀表盤添加英雄
為了使儀表盤更有趣翼馆,你會一眼就能看到四個頂級英雄割以。
使用templateUrl
屬性替換template
元數(shù)據(jù),它將指向一個新的模板文件写妥,同時添加如下所示的指令(很快你會添加必要的導(dǎo)入):
// lib/src/dashboard_component.dart (metadata)
@Component(
selector: 'my-dashboard',
templateUrl: 'dashboard_component.html',
directives: const [CORE_DIRECTIVES, ROUTER_DIRECTIVES],
)
templateUrl
的值可以是這個包或其它包的資源拳球。當(dāng)使用另一個包的資源時审姓,使用完整的包引用珍特,例如:'package:some_other_package/dashboard_component.html'
。
創(chuàng)建的模板文件包含以下內(nèi)容:
// lib/src/dashboard_component.html
<h3>Top Heroes</h3>
<div class="grid grid-pad">
<div *ngFor="let hero of heroes">
<div class="module hero">
<h4>{{hero.name}}</h4>
</div>
</div>
</div>
再次使用*ngFor
來遍歷一個英雄列表魔吐,并顯示它們的名字扎筒。額外的<div>
元素,有助于稍后的樣式美化酬姆。
共享 HeroService
你可以再次使用HeroService
來填充組件的heroes
列表嗜桌。
早先,從HeroesComponent
的providers
列表中移除了HeroService
辞色,并把它添加到AppComponent
的providers
列表中骨宠。這個移動創(chuàng)建了一個單獨的HeroService
實例,它對應(yīng)用中的所有組件都有效相满。Angular 會注入HeroService
层亿,然后你可以在DashboardComponent
中使用它了。
獲取英雄數(shù)據(jù)
在dashboard_component.dart
添加如下import
語句立美。
// lib/src/dashboard_component.dart (imports)
import 'dart:async';
import 'package:angular/angular.dart';
import 'package:angular_router/angular_router.dart';
import 'hero.dart';
import 'hero_service.dart';
現(xiàn)在創(chuàng)建DashboardComponent
類匿又,像這樣:
// lib/src/dashboard_component.dart (class)
class DashboardComponent implements OnInit {
List<Hero> heroes;
final HeroService _heroService;
DashboardComponent(this._heroService);
Future<Null> ngOnInit() async {
heroes = (await _heroService.getHeroes()).skip(1).take(4).toList();
}
}
這種邏輯也用于HeroesComponent
:
- 定義一個
heroes
列表屬性。 - 把
HeroService
注入到構(gòu)造函數(shù)建蹄,并且把它保存在一個私有的_heroService
字段中碌更。 - 在 Angular 的
ngOnInit()
生命周期鉤子里,調(diào)用服務(wù)獲取英雄洞慎。
在這個儀表盤中痛单,指定了四個英雄(第 2 、 3 劲腿、 4 旭绒、 5 個)。
刷新瀏覽器谆棱,在這個新的儀表盤中會看到四個英雄快压。
導(dǎo)航到英雄詳情
雖然所選英雄的詳情顯示在了HeroesComponent
的底部,但用戶應(yīng)該能夠通過以下額外的方式導(dǎo)航到HeroDetailComponent
:
- 從儀表盤到所選英雄垃瞧。
- 從英雄列表到所選英雄蔫劣。
- 從粘貼到瀏覽器地址欄的“深鏈接” URL。
路由到一個英雄詳情
在AppComponent
中定義其它路由的地方个从,添加一個到HeroDetailComponent
的路由脉幢。
這個新路由的不尋常之處在于歪沃,必須告訴HeroDetailComponent
該顯示哪個英雄。你不需要告訴 HeroesComponent
或DashboardComponent
任何事情嫌松。
現(xiàn)在沪曙,父組件HeroesComponent
使用如下綁定設(shè)置組件的hero
屬性到一個英雄對象:
<hero-detail [hero]="selectedHero"></hero-detail>
但這種綁定在任意路由場景中都無法工作。
參數(shù)化路由
你可以添加英雄的id
到路由路徑中萎羔。當(dāng)路由到一個id
為 11 的英雄時液走,你可能期望看到像這樣的路徑:
/detail/11
/detail/
部分是固定不變的。但后面跟著的數(shù)字id
部分會隨著英雄的不同而變化贾陷。你需要使用代表英雄id
的參數(shù) 來表示路由的可變部分缘眶。
添加帶參數(shù)的路由
首先,導(dǎo)入英雄詳情組件:
import 'src/hero_detail_component.dart';
然后髓废,添加如下路由:
// lib/app_component.dart (HeroDetail route)
const Route(
path: '/detail/:id',
name: 'HeroDetail',
component: HeroDetailComponent,
),
路徑中的冒號(:
)表明:id
是一個導(dǎo)航到HeroDetailComponent
時巷懈,特定英雄id
的占位符。
你已經(jīng)完成了本應(yīng)用的路由慌洪。
你沒有往模板中添加一個英雄詳情鏈接顶燕,這是因為用戶不會直接點擊一個導(dǎo)航鏈接 去查看一個特定的英雄;他們只會點擊英雄名冈爹,不論是顯示在儀表盤上的名字還是在英雄列表中的名字涌攻。但這并不工作,直到HeroDetailComponent
被修改好并且能夠被導(dǎo)航過去犯助。
修改 HeroDetailComponent
這里是HeroDetailComponent
現(xiàn)在的樣子:
// lib/src/hero_detail_component.dart (current)
import 'package:angular/angular.dart';
import 'package:angular_forms/angular_forms.dart';
import 'hero.dart';
@Component(
selector: 'hero-detail',
template: '''
<div *ngIf="hero != null">
<h2>{{hero.name}} details!</h2>
<div><label>id: </label>{{hero.id}}</div>
<div>
<label>name: </label>
<input [(ngModel)]="hero.name" placeholder="name"/>
</div>
</div>
''',
directives: const [CORE_DIRECTIVES, formDirectives],
)
class HeroDetailComponent {
@Input()
Hero hero;
}
模板不用修改癣漆。英雄名會用同樣的方式顯示。主要的變化是如何獲取英雄名剂买。
你不會再從父組件的屬性綁定中接收英雄惠爽,所以你可以從hero
字段移除 @Input() 注解:
// lib/src/hero_detail_component.dart (hero with @Input removed)
class HeroDetailComponent implements OnInit {
Hero hero;
}
新的HeroDetailComponent
會從路由器的RouteParams
服務(wù)中得到id
參數(shù),并使用HeroService
瞬哼,通過這個id
來獲取英雄婚肆。
添加下面的導(dǎo)入:
// lib/src/hero_detail_component.dart (added-imports)
import 'dart:async';
import 'package:angular_router/angular_router.dart';
import 'hero_service.dart';
把 RouteParams、HeroService
和 Location 服務(wù)注入到構(gòu)造函數(shù)中坐慰,并將它們的值保存到私有字段:
// lib/src/hero_detail_component.dart (constructor)
final HeroService _heroService;
final RouteParams _routeParams;
final Location _location;
HeroDetailComponent(this._heroService, this._routeParams, this._location);
告訴這個類较性,要實現(xiàn)OnInit
接口。
class HeroDetailComponent implements OnInit {
在ngOnInit
生命周期鉤子中结胀,從RouteParams
服務(wù)中提取id
參數(shù)值赞咙,并且使用HeroService
通過這個id
來獲取英雄。
// lib/src/hero_detail_component.dart (ngOnInit)
Future<Null> ngOnInit() async {
var _id = _routeParams.get('id');
var id = int.parse(_id ?? '', onError: (_) => null);
if (id != null) hero = await (_heroService.getHero(id));
}
注意我們是怎樣通過調(diào)用RouteParams.get()
方法提取id
的糟港。
英雄的id
是一個數(shù)字攀操。而路由參數(shù)總是字符串。所以路由參數(shù)的值被轉(zhuǎn)換成了數(shù)字秸抚。
添加 HeroService.getHero()
在ngOnInit()
中速和,你使用了一個HeroService
還沒有的getHero()
方法歹垫。打開HeroService
并添加一個通過id
從getHeroes()
過濾英雄列表的getHero()
方法。
// lib/src/hero_service.dart (getHero)
Future<Hero> getHero(int id) async =>
(await getHeroes()).firstWhere((hero) => hero.id == id);
找到回來的路
用戶有多種方式導(dǎo)航到HeroDetailComponent
颠放。
用戶可以點擊AppComponent
中的兩個鏈接排惨,或點擊瀏覽器的“后退”按鈕,來導(dǎo)航到其它地方∨鲂祝現(xiàn)在添加第三種方式暮芭,一個goBack()
方法,它使用之前注入的Location
服務(wù)在瀏覽器的歷史棧中后退一步痒留。
// lib/src/hero_detail_component.dart (goBack)
void goBack() => _location.back();
回退太多步可能會使用戶離開應(yīng)用谴麦。在真實的應(yīng)用種蠢沿,你可以使用 routerCanDeactivate() 鉤子來阻止這個問題伸头。更多內(nèi)容請看 CanDeactivate 章節(jié)。
在組件模板中添加 Back 按鈕舷蟀,并使用事件綁定綁定到這個方法恤磷。
<button (click)="goBack()">Back</button>
把模板移到它自己的hero_detail_component.html
文件中:
// lib/src/hero_detail_component.dart (metadata)
<div *ngIf="hero != null">
<h2>{{hero.name}} details!</h2>
<div>
<label>id: </label>{{hero.id}}</div>
<div>
<label>name: </label>
<input [(ngModel)]="hero.name" placeholder="name" />
</div>
<button (click)="goBack()">Back</button>
</div>
更新組件的元數(shù)據(jù),使用templateUrl
指向剛剛創(chuàng)建的模板文件野宜。
// lib/src/hero_detail_component.dart (metadata)
@Component(
selector: 'hero-detail',
templateUrl: 'hero_detail_component.html',
directives: const [CORE_DIRECTIVES, formDirectives],
)
刷新瀏覽器并訪問 localhost:8080/#detail/11扫步。11號英雄的詳情會被顯示。在儀表盤或是英雄列表中選擇英雄還不起作用匈子。接下來你將處理這個問題河胎。
選擇一個儀表盤中的英雄
當(dāng)用戶從儀表盤中選擇了一位英雄時,應(yīng)用應(yīng)該導(dǎo)航到HeroDetailComponent
以允許用戶查看和編輯所選的英雄虎敦。
儀表盤英雄的行為應(yīng)該像錨標(biāo)簽一樣:當(dāng)鼠標(biāo)移動到一個英雄上時游岳,目標(biāo) URL 應(yīng)該顯示在瀏覽器的狀態(tài)欄上,并且用戶應(yīng)該能復(fù)制鏈接或者在新標(biāo)簽頁打開英雄詳情視圖其徙。
要實現(xiàn)這種效果胚迫,打開dashboard.component.html
,使用錨點代替<div *ngFor...>
(子元素保持不變):
// lib/src/dashboard_component.html (repeated <a> tag)
<a *ngFor="let hero of heroes" [routerLink]="['HeroDetail', {id: hero.id.toString()}]" class="col-1-4">
<div class="module hero">
<h4>{{hero.name}}</h4>
</div>
</a>
注意 [routerLink]
綁定唾那。正如本章路由鏈接 部分所述访锻,AppComponent
模板中的頂級導(dǎo)航有兩個設(shè)置為固定的目標(biāo)路由名,/dashboard
和/heroes
闹获。
這次期犬,你綁定到了一個包含鏈接參數(shù)列表的表達(dá)式。該列表有兩個元素:目標(biāo)路由名字和一個設(shè)置為當(dāng)前英雄id
值的路由參數(shù)避诽。
這兩個列表項分別對應(yīng)之前在參數(shù)化英雄詳情路由定義中添加的name和:id:
// lib/app_component.dart (HeroDetail route)
const Route(
path: '/detail/:id',
name: 'HeroDetail',
component: HeroDetailComponent,
),
刷新瀏覽器龟虎,并從儀表盤中選擇一位英雄;應(yīng)用就會導(dǎo)航到該英雄的詳情茎用。
在 HeroesComponent 中選擇一位英雄
在HeroesComponent
中遣总,當(dāng)前模板展示了一個"主從"風(fēng)格的視圖:上方是英雄列表睬罗,底下是所選英雄的詳情。
// lib/src/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>
<hero-detail [hero]="selectedHero"></hero-detail>
這里你將不再展示完整的HeroesComponent
旭斥。相反容达,你會在它自己的頁面顯示英雄詳情,并像在儀表盤中所做的路由到它垂券。做以下改變:
- 從模板的最后一行刪除
<hero-detail>
元素花盐。 - 從
directives
列表中移除HeroDetailComponent
。 - 移除英雄詳情導(dǎo)入菇爪。
當(dāng)用戶從列表中選擇一個英雄時算芯,他們并不會進入詳情頁。相反凳宙,他們會在本頁看到一個迷你的英雄詳情熙揍,并且必須點擊一個按鈕來導(dǎo)航到完整的英雄詳情頁。
添加迷你 英雄詳情
在模板底部原來放<hero-detail>
的地方添加下列 HTML 片段:
// lib/src/heroes_component.html (mini detail)
<div *ngIf="selectedHero != null">
<h2>
{{selectedHero.name | uppercase}} is my hero
</h2>
<button (click)="gotoDetail()">View Details</button>
</div>
在HeroesComponent
中添加如下方法:
// lib/src/heroes_component.dart (gotoDetail stub)
Future<Null> gotoDetail() => null;
稍后氏涩,點擊一個英雄(但現(xiàn)在別做届囚,因為它還不工作),用戶應(yīng)該能在英雄列表下方看到像下面的樣子:
英雄的名字被顯示成大寫字母是尖,因為在插值表達(dá)式綁定中意系,管道操作符(|
)的后面,包含了uppercase
管道饺汹。
{{selectedHero.name | uppercase}} is my hero
管道是一個格式化字符串蛔添、貨幣金額、日期和其它顯示數(shù)據(jù)的好方法兜辞。Angular 自帶了幾個管道迎瞧,并且你可以寫自己的管道。
在你能夠在模板中使用 Angular 管道之前弦疮,你需要在組件的@Component
注解的參數(shù)pipes
中列出要使用的管道夹攒。你可以添加單獨的管道,或者使用更方便的管道集合如 COMMON_PIPES胁塞。
// lib/src/heroes_component.dart (pipes)
@Component(
selector: 'my-heroes',
pipes: const [COMMON_PIPES],
)
更多關(guān)于管道的內(nèi)容請看 Pipes咏尝。
刷新瀏覽器。從英雄列表中選擇一個英雄將會激活迷你詳情視圖⌒グ眨現(xiàn)在查看詳情按鈕還不起作用编检。
更新 HeroesComponent 類
點擊按鈕時,HeroesComponent
導(dǎo)航到HeroesDetailComponent
扰才。該按鈕的點擊事件被綁定到gotoDetail()
方法允懂,它通過告訴路由器應(yīng)該去哪兒進行命令式地導(dǎo)航。
該方法需要對組件類做以下改變:
- 導(dǎo)入 angular_router衩匣。
- 和
HeroService
一起蕾总,在構(gòu)造函數(shù)中注入Router
粥航。 - 通過調(diào)用路由器的
navigate()
方法,實現(xiàn)gotoDetail()
生百。
下面是修改后的HeroesComponent
類:
// lib/src/heroes_component.dart (class)
class HeroesComponent implements OnInit {
final HeroService _heroService;
final Router _router;
List<Hero> heroes;
Hero selectedHero;
HeroesComponent(
this._heroService,
this._router
);
Future<Null> getHeroes() async {
heroes = await _heroService.getHeroes();
}
void ngOnInit() => getHeroes();
void onSelect(Hero hero) => selectedHero = hero;
Future<Null> gotoDetail() => _router.navigate([
'HeroDetail',
{'id': selectedHero.id.toString()}
]);
}
在gotoDetail()
中递雀,你往路由器的navigate()
方法中傳遞了一個有兩個元素的鏈接參數(shù)列表——一個路由名和這個路由的參數(shù),就和之前在DashboardComponent
中使用[routerLink]
綁定所做的一樣蚀浆。
刷新瀏覽器缀程,并開始點擊。用戶能在應(yīng)用中導(dǎo)航:從儀表盤到英雄詳情再回來市俊,從英雄列表到迷你英雄詳情杨凑,再到英雄詳情,再回到英雄列表摆昧。
你已經(jīng)滿足了在本章開頭設(shè)定的所有導(dǎo)航需求撩满。
給應(yīng)用添加樣式
應(yīng)用的功能已經(jīng)完成了,但它需要添加樣式据忘。儀表盤英雄應(yīng)該顯示在一行的矩形中鹦牛。你會看到大約 60 行 CSS 來實現(xiàn)它,包括一些為響應(yīng)式設(shè)計而寫的簡單的媒體查詢勇吊。
正如你所知道的,在組件的styles
元數(shù)據(jù)中添加這些 CSS 會使組件邏輯模糊不清窍仰。所以汉规,在一個獨立的.css
文件中添加這些 CSS。
儀表盤樣式
在lib/src
目錄下創(chuàng)建一個dashboard_component.css
文件驹吮,并在組件元數(shù)據(jù)的styleUrls
列表屬性中引用它针史,就像這樣:
// lib/src/dashboard_component.dart (styleUrls)
@Component(
selector: 'my-dashboard',
templateUrl: 'dashboard_component.html',
styleUrls: const ['dashboard_component.css'],
directives: const [CORE_DIRECTIVES, ROUTER_DIRECTIVES],
)
// lib/src/dashboard_component.css
[class*='col-'] {
float: left;
text-decoration: none;
padding-right: 20px;
padding-bottom: 20px;
}
[class*='col-']:last-of-type {
padding-right: 0;
}
*, *:after, *:before {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
h3 {
text-align: center; margin-bottom: 0;
}
h4 {
position: relative;
}
.grid {
margin: 0;
}
.col-1-4 {
width: 25%;
}
.module {
padding: 20px;
text-align: center;
color: #eee;
max-height: 120px;
min-width: 120px;
background-color: #607D8B;
border-radius: 2px;
}
.module:hover {
background-color: #EEE;
cursor: pointer;
color: #607d8b;
}
.grid-pad {
padding: 10px 0;
}
.grid-pad > [class*='col-']:last-of-type {
padding-right: 20px;
}
@media (max-width: 600px) {
.module {
font-size: 10px;
max-height: 75px; }
}
@media (max-width: 1024px) {
.grid {
margin: 0;
}
.module {
min-width: 60px;
}
}
英雄詳情樣式
在lib/src
目錄下創(chuàng)建一個hero_detail_component.css
文件,并且在組件元數(shù)據(jù)的styleUrls
列表中引用它:
// lib/src/hero_detail_component.dart(styleUrls)
@Component(
selector: 'hero-detail',
templateUrl: 'hero_detail_component.html',
styleUrls: const ['hero_detail_component.css'],
directives: const [CORE_DIRECTIVES, formDirectives],
)
// lib/src/hero_detail_component.css
label {
display: inline-block;
width: 3em;
margin: .5em 0;
color: #607D8B;
font-weight: bold;
}
input {
height: 2em;
font-size: 1em;
padding-left: .4em;
}
button {
margin-top: 20px;
font-family: Arial;
background-color: #eee;
border: none;
padding: 5px 10px;
border-radius: 4px;
cursor: pointer; cursor: hand;
}
button:hover {
background-color: #cfd8dc;
}
button:disabled {
background-color: #eee;
color: #ccc;
cursor: auto;
}
給導(dǎo)航鏈接添加樣式
在lib
目錄下創(chuàng)建一個app_component.css
文件碟狞,并且在組件元數(shù)據(jù)的styleUrls
列表中引用它:
// lib/app_component.dart(styleUrls)
styleUrls: const ['app_component.css'],
// lib/app_component.css
h1 {
font-size: 1.2em;
color: #999;
margin-bottom: 0;
}
h2 {
font-size: 2em;
margin-top: 0;
padding-top: 0;
}
nav a {
padding: 5px 10px;
text-decoration: none;
margin-top: 10px;
display: inline-block;
background-color: #eee;
border-radius: 4px;
}
nav a:visited, a:link {
color: #607D8B;
}
nav a:hover {
color: #039be5;
background-color: #CFD8DC;
}
nav a.router-link-active {
color: #039be5;
}
提供的 CSS 使得在AppComponent
中的導(dǎo)航鏈接看起來更像是可選按鈕啄枕。早前,你使用了一個<nav>
元素包圍著這些鏈接:
router-link-active CSS 類
Angular 路由器添加
router-link-active
CSS 類到那些路由匹配激活的路由的 HTML 導(dǎo)航元素族沃。你唯一要做的就是為它定義樣式频祝。
應(yīng)用的全局樣式
當(dāng)你給一個組件添加樣式時,你要使組件所需的一切——HTML脆淹、CSS常空、程序代碼,都集中放在一個方便的地方盖溺。這樣漓糙,無論是把它們打包起來還是在別的其它地方復(fù)用這個組件都會很容易。
你也可以在所有組件之外創(chuàng)建應(yīng)用級別的樣式烘嘱。
設(shè)計師提供了一些基本的樣式昆禽,適用于貫穿整個應(yīng)用的元素蝗蛙。這些和之前在配置開發(fā)環(huán)境中安裝的全套主樣式一致。下面是摘錄:
// web/styles.css(excerppt)
@import url(https://fonts.googleapis.com/css?family=Roboto);
@import url(https://fonts.googleapis.com/css?family=Material+Icons);
/* Master Styles */
h1 {
color: #369;
font-family: Arial, Helvetica, sans-serif;
font-size: 250%;
}
h2, h3 {
color: #444;
font-family: Arial, Helvetica, sans-serif;
font-weight: lighter;
}
body {
margin: 2em;
}
body, input[text], button {
color: #888;
font-family: Cambria, Georgia;
}
/* ··· */
/* everywhere else */
* {
font-family: Arial, Helvetica, sans-serif;
}
如有必要的話醉鳖,創(chuàng)建web/styles.css
文件歼郭。確保文件中包含 master styles provided here 的內(nèi)容。并且編輯web/index.html
來引用這個樣式辐棒。
// web/index.html(link ref)
<link rel="stylesheet" href="styles.css">
現(xiàn)在看看這個應(yīng)用病曾。儀表盤、英雄和導(dǎo)航鏈接都被應(yīng)用了樣式漾根。
應(yīng)用結(jié)構(gòu)和代碼
在在線示例 (查看源碼)中檢查本章的示例源代碼泰涂。驗證你是否已經(jīng)有如下結(jié)構(gòu):
angular_tour_of_heroes/
|___lib/
| |___app_component.{css,dart}
|___src/
| | |___dashboard_component.{css,dart,html}
| | |___hero.dart
| | |___hero_detail_component.{css,dart,html}
| | |___hero_service.dart
| | |___heroes_component.{css,dart,html}
| | |___mock_heroes.dart
|___test/
| |___app_test.dart
| |___...
|___web/
| |___index.html
| |___main.dart
| |___styles.css
|___pubspec.yaml
走過的路
以下是你在本章中完成的:
- 添加 Angular 路由,在不同組件之間導(dǎo)航辐怕。
- 學(xué)會了如何創(chuàng)建路由鏈接來表示導(dǎo)航欄的菜單項逼蒙。
- 使用路由鏈接參數(shù)來導(dǎo)航到用戶所選英雄的詳情。
- 在多個組件之間共享
HeroService
服務(wù)寄疏。 - 添加
uppercase
管道來格式化數(shù)據(jù)是牢。
你的應(yīng)用看起來應(yīng)該是這樣在線示例 (查看源碼)。
下一步