[Angular2官方tutorial學習04]Routing:RouterModule.forRoot,router-outlet的template樣版概念
雖然聲稱SPA,不過當然還是有很多的頁面功能需要切換來切換去,這時候routing就要出場囉
@執行下列指令以產生routing的模組
一般來說會用app-routing這個名稱來當作routing的模組,flat則是讓angular cli在產生相關檔案的時候,不要新增routing專用的資料夾並放在裡面,而是直接放在src/app資料夾裡面,module=app則是請angular cli順便r加入相關的import到AppModule(即src\app\app.module.ts)
ng generate module app-routing --flat --module=app
接著打開剛剛被anglar cli新增的檔案src/app/app-routing.module.ts,把原本的內容全部刪除後,取代成下面這樣,這就是for routing專用的基本設定
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
@NgModule({
exports: [ RouterModule ]
})
export class AppRoutingModule {}
@接著來試著把我們剛才的Hero網頁加入routing之中,原本我們的網址是localhost:4200,現在要把他改為localhost:4200/heroes
同樣在src/app/app-routing.module.ts檔案裡,加入下列內容
import { HeroesComponent } from './heroes/heroes.component';
const routes: Routes = [
{ path: 'heroes', component: HeroesComponent }
];
再把下面這段import加入@NgModule裡,這段的功用是讓router開始監控網址的變化
imports: [ RouterModule.forRoot(routes) ],
最後src\app\app-routing.module.ts檔案看起來會像是這樣
@接著打開src/app/app.component.html,把<app-heroes></app-heroes>取代為下面
router-outlet關鍵字一旦出現,可以說就是以往asp.net webform或是asp.net mvc的template樣版的概念出現了!
<router-outlet></router-outlet>
最後src\app\app.component.html看起來如下
此時的localhost:4200不再載入heros清單,當你開啟網址localhost:4200/heroes的時候,才會載入heros清單,而且載入的位置就是會在router-outlet
此時index.html變的比較像是asp.net MVC的樣版的功能或是asp.net web forms的master template的樣版功能
此時打開網頁http://localhost:4200/heroes,heros清單出現囉
這時候如果你去打開localhost:4200的話,是不會看到任何heros的喔:
@然後是要試著加入我們第一個超連結,打開src/app/app.component.html之後,將下面超連結加入在<router-outlet></router-outlet>的上面
<nav>
<a routerLink="/heroes">Heroes</a>
</nav>
加好之後長這樣
這個nav超連結heros的位置就會出現在<router-outlet></router-outlet>的位置囉
打開瀏覽器localhost:4200一看,果然有heros的超連結囉!
@再來我們要加入一個dashboard計分版component,請執行下列angular cli指令
dashboard是用來顯示排行前幾名的hero的,之後會在src\app\dashboard\dashboard.component.ts裡面會設定為取得top 4前四筆
ng g c dashboard
其實dashboard也是用來顯示hero的清單,只有運用了到目前為止所學習到的angular2的知識,並沒有用到其他新的知識,很簡單,因此這邊就不多做說明,請直接把下面三個檔案的內容自行貼上喔
1. src/app/dashboard/dashboard.component.html
<h3>Top Heroes</h3>
<div class="grid grid-pad">
<a *ngFor="let hero of heroes" class="col-1-4">
<div class="module hero">
<h4>{{hero.name}}</h4>
</div>
</a>
</div>
2. src/app/dashboard/dashboard.component.ts
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() {
this.getHeroes();
}
getHeroes(): void {
this.heroService.getHeroes()
.subscribe(heroes => this.heroes = heroes.slice(1, 5));
}
}
3. src/app/dashboard/dashboard.component.css
/* DashboardComponent's private CSS styles */
[class*='col-'] {
float: left;
padding-right: 20px;
padding-bottom: 20px;
}
[class*='col-']:last-of-type {
padding-right: 0;
}
a {
text-decoration: none;
}
*, *: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;
}
}
dashboard加入這個元件之後,當然要幫他的routing也設定一下,再次打開src/app/app-routing.module.ts,import下面這個
import { DashboardComponent } from './dashboard/dashboard.component';
然後把path的設定也加入到const routes: Routes這個陣列裡面
{ path: 'dashboard', component: DashboardComponent },
既然都叫做dashboard了,當然預設的首頁就要自動導向他,因此繼續把下面的path設定也加入const routes: Routes這個陣列裡面
下面的寫法就是當path是fully完全的等於空字串的時候,重新導向到/dashboard這個網址
{ path: '', redirectTo: '/dashboard', pathMatch: 'full' },
最後src\app\app-routing.module.ts的內容長這樣
最後再於src/app/app.component.html加入一個dashboard超連結,讓在heros頁面的使用者可以直接連回dashboard喔
<a routerLink="/dashboard">Dashboard</a>
這時候再打開瀏覽器首頁localhost:4200一看,果然自動導向到dashboard囉
@下一步是要把hero detail獨立來到另外一個routing網址喔
首先打開heroes/heroes.component.html,把裡面的<app-hero-detail>刪除,然後打開瀏覽器一看,果然hero的detail資訊都不見了
然後打開src/app/app-routing.module.ts,加入下列hero detail的import
import { HeroDetailComponent } from './hero-detail/hero-detail.component';
並且把下列路徑設定加入到const routes: Routes這個陣列裡面
{ path: 'detail/:id', component: HeroDetailComponent },
以上都加進去之後,src\app\app-routing.module.ts看起來結果像是這樣
然後要在dashboard顯示的hero清單加入超連結,每一筆hero都需要包含一個超連結去連到herodetail網頁,請打開src/app/dashboard/dashboard.component.html
把*ngFor那段改成下面這樣,你可以發現其實就是加入一個routerLink="/detail/{{hero.id}}"的超連結而已
<a *ngFor="let hero of heroes" class="col-1-4"
routerLink="/detail/{{hero.id}}">
<div class="module hero">
<h4>{{hero.name}}</h4>
</div>
</a>
同樣的如法炮製在src/app/heroes/heroes.component.html,也幫他加入一個routerLink="/detail/{{hero.id}}",加完之後長這樣
<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>
接著再貼入官方提供最新版的heroes.component.css
/* 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;
background-color: #EEE;
margin: .5em;
padding: .3em 0;
height: 1.6em;
border-radius: 4px;
}
.heroes li:hover {
color: #607D8B;
background-color: #DDD;
left: .1em;
}
.heroes a {
color: #888;
text-decoration: none;
position: relative;
display: block;
width: 250px;
}
.heroes a:hover {
color:#607D8B;
}
.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;
min-width: 16px;
text-align: right;
margin-right: .8em;
border-radius: 4px 0 0 4px;
}
@而原本herodetail的component是透過在同一個頁面下的heros component的click事件來取得該hero的資料,不過現在已經把hero跟herodetail切割成不同的頁面了,所以click事件應該要刪除,打開src/app/heroes/heroes.component.ts,把selectedHero變數刪除,還有把onSelect()事件也刪除
@再來下一步當然是要讓hero detail元件能夠有辦法讀取到db的hero資料,方能正確的在畫面上顯示,打開src/app/hero-detail/hero-detail.component.ts
加入下面三個import
import { ActivatedRoute } from '@angular/router';
import { Location } from '@angular/common';
import { HeroService } from '../hero.service';
並且在constructor裡面加入這三行服務(官方叫做inject service,真是OOXX不習慣的說法,有夠饒舌)
ActivateRoute關鍵字就是用來接收網址的id參數,例如localhost:4200/detailhero/id的id參數
Location關鍵字是內建的angular service,專門用來做回到上一頁功能的
private route: ActivatedRoute,
private heroService: HeroService,
private location: Location
加好之後,src\app\hero-detail\hero-detail.component.ts最後長這樣
繼續在ngOnInit裡面呼叫getHero(id)用來取得資料(只是getHero(id)的service還沒建立就是了,下一步就會建立了)
ngOnInit(): void {
this.getHero();
}
getHero(): void {
const id = +this.route.snapshot.paramMap.get('id');
this.heroService.getHero(id)
.subscribe(hero => this.hero = hero);
}
@這一步驟當然就是要在服務建立getHero(id),打開src/app/hero.service.ts,加入下面這個method
注意一下messageService.add裡面用的是反引號`,這是為了要把${id}動態的加入到訊息當中,沒什麼特別,就是方便查看狀態而已
getHero(id: number): Observable<Hero> {
// TODO: send the message _after_ fetching the hero
this.messageService.add(`HeroService: fetched hero id=${id}`);
return of(HEROES.find(hero => hero.id === id));
}
此時打開瀏覽器一看,hero detail專用網址已經正常運作囉 XD
@剛剛有提到回到上一頁的功能,現在當然要加入,請打開src/app/hero-detail/hero-detail.component.html
在*ngIf裡面加入一個回到上一頁按鈕
<button (click)="goBack()">go back</button>
並且設定回到上一頁的功能,請打開src/app/hero-detail/hero-detail.component.ts
定義一下goBack()這個method,稍微注意一下:location.back()是用angular service內建的回到上一頁功能實做的喔,這並非傳統javascript的回到上一頁的語法
goBack(): void {
this.location.back();
}
最後src\app\hero-detail\hero-detail.component.ts長這樣
當然,回到上一頁的按鈕也出現了!
結束了!官方的angular tutorial的service部分的課程終於結束了!
謝天謝地
不過後面還有最後一個http的部分的課程,是關於如何使用http client Orz
附上最後完成的程式碼的超連結:(這三個的程式碼應該都幾乎一樣)
官網的live example
官網提供的整個壓縮檔直接下載
我這邊的dropbox直接下載
參考資料:
Add the AppRoutingModule - Angular Official Tutorial
https://angular.io/tutorial/toh-pt5