https://www.youtube.com/watch?v=t7MEvi9RdNI&list=PL9LUW6O9WZqgUMHwDsKQf3prtqVvjGZ6S&index=7
今天是由Leo老師主講,「路由」可參考[Angular 深入淺出三十天] Day 20 ~ Day 26
https://ithelp.ithome.com.tw/users/20090728/ironman/1600
以前就覺得[Angular 深入淺出三十天]寫得超棒,例子很棒,超好懂
想不到看到這一集路由就由Leo來講解!!
如果Leo老師文章中的內容,我就會附那篇的連結 (除了避免抄襲,也剛好可以偷懶一下)
除非是範例我覺得可以再加註解的(讓自己跟新手們更好懂)
首先,先開好片頭導讀的文章,再跟著影片一起看,以後才有能力自己查文件
今天內容有:(方便search)
用XMind畫的 for mac
https://apps.apple.com/tw/app/xmind-zen-mind-mapping/id1327661892?mt=12
root iT邦幫忙 /
Layout /
技術問答
技術文章
iT徵才
iT活動
Tag
鐵人賽
登入頁 /
<ul>
<li><a routerLink="login">登入頁</a></li> routerLink為directive
<li><a [routerLink]="'login'">登入頁</a></li> 屬性寫法,可指定陣列"['home','xxx']"
<li><a routerLink="">Layout</a></li>
<li><a routerLink="questions">技術問答</a></li>
<!-- <li><a routerLink="article?tab=tech">技術文章</a></li> -->
<li><a routerLink="article" [queryParams]="{ tab: 'tech'}">技術文章</a></li>
<li><a routerLink="article/tag">iT 徵才</a></li>
<li><a routerLink="article?tab=event">iT 活動</a></li>
<li><a routerLink="tags" routerLinkActive="active">Tags</a></li> active作為css類別
會幫<a>動態加上.active
a.active { background-color: grey}
當輸入 http://localhost:4200/layout/tag
則/layout跟/layout/tag都會符合路由規則
要加上這個 [routerLinkActiveOptions]="{exact:true}" 才會只有/layout/tag 符合
</ul>
constructor(
private route: ActivatedRoute // 目前路徑資訊
private router: Router // 在ts裡路由
){}
ngOnInit(){
// 參數取法1:可以接參數 [queryParams]="{ tab: 'tech'}"
this.route.queryParams.subscribe((params) => {
params 會是物件 { tab: 'tech'}
}
// 參數取法2:
this.route.snapshot.queryParams; // 一樣取到 物件 { tab: 'tech'}
this.route.snapshot.queryParams['tab']; // 取到'tech'
// 補充: 取變數 { path: 'article/:id }
this.route.snapshot.paramMap.get('id'); // paramMap是所有參數的地圖
// private router: Router // 在ts裡路由
this.router.navigate(['login']); /login
this.router.navigate(['article','tag']); /article/tag
// 使用navigateByUrl
this.router.navigateByUrl('login');
// 使用相對路徑redirect
this.router.navigate(['..','login'],{
relativeTo: this.route // 當前路由
})
}
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
...
const routes: Routes = [
// Layout
{
path: '',
component: LayoutComponent
children: [
{
path: 'question',
component: QuestionComponent
},
{
path: 'articles',
component: ArticlesComponent
},
{
path: 'tags',
component: TagsComponent
},
]
},
// 登入頁
{ path: 'login', component: LoginComponent},
{
path: '**', // 萬用路由
redirectTo: 'login', // 重導向,若用到reidrectTo,則一定要有pathMatch
pathMatch: 'full' // 預設為prefix,確保只有當其餘路徑與空字串相符時,才會redirect
}
]; // Routes 是一個路由組態的陣列
// VS Code對Routes右鍵,Go to Definition
// 可跳到 node_modules/@angular/router/src/config.d.ts
// 可看 Routes 的 interface,來快速了解有哪些東西可以用
// export interface Route {
// path?: string;
// pathMatch?: string;
// matcher?: UrlMatcher;
// component?: Type<any>;
// redirectTo?: string;
// outlet?: string;
// canActivate?: any[];
// canActivateChild?: any[];
// canDeactivate?: any[];
// canLoad?: any[];
// data?: Data;
// resolve?: ResolveData;
// children?: Routes;
// loadChildren?: LoadChildren;
// runGuardsAndResolvers?: RunGuardsAndResolvers;
// }
@NgModule({
imports: [RouterModule.forRoot(routes,{
enableTracing: ture, // 開啟 router log內部事件 顯示在console裡
useHash: true // 支援IE6,網址會有#,改用 HashLocationStrategy
// 使用情境:server無法支援spa架構時
// 預設 useHash: false 使用官方建議的 PathLocationStrategy
// 要設定url rewrite(要回到dist/index.html),否則按F5會跑到404
// PathLocationStrategy 的好處
// 在路由狀態改變的時候,會加在history.pushState,不會真的向伺服器送出請求
// 載入所有模組(跟lazy loading應用情境相反)
// 使用情境: 專案較小,或不想在使用時卡卡的
preloadingStrategy: PreloadAllModules
})],
exports: [RouterModule]
})
export class AppRoutingModule { }
偷懶一下,請參考,看文章很好懂喔
[Angular 深入淺出三十天] Day 22 - 路由(五)
https://ithelp.ithome.com.tw/articles/10208267
{
path: 'login',
loadChildren: './login/login.module#LoginModule'
}
Guard 也是 Service 的一種,本節講3個東西:
ng g guard guards/auth
import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs';
import { UserStoreService } from '../serivces/user-store.service';
@Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate {
constructor(
private userStore: UserStoreService, // 假設有一個user的service
private router: Router
){}
canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot // 當前路由資訊
): Observable<boolean> | Promise<boolean> | boolean // 回傳true|false
{
// 如果是Login狀態,就回傳ture
if(this.userStore.isLoggedIn()) {return true};
// 若不是Login狀態
this.router.navigate(['login]); // redirect回login
return false;
}
}
import { AuthGuard } from './guards/auth.guard';
@NgModule({
...
// AuthGuard是Service,要加在providers
providers: [AuthGuard], // ng g guard不會自動加,要自已加
...
})
export class AppModule{}
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { AuthGuard } from './guards/auth.guard';
const routes: Routes = [
{ path: '', redirectTo: '/login', pathMatch: 'full'},
{ path: 'login', component: LoginComponent },
{
path: 'article/tag',
component: ArticleTagComponent,
canActivate: [AuthGuard] // 會去呼叫canActivate(),回傳true就能進去article/tag
},
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
跟CanActivate()不同的是,實作一個AuthGuard就能通用
而CanDeactivate要每個Component寫一個XXXXDeactiveGuard
(可能有泛型的方式傳入Component的interface到DeactiveGuard,但我不會,有的朋友可提供文章連結喔)
1.some-deactivate.guard.ts
import { Injectable } from '@angular/core';
import { CanDeactivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs';
import { SomeComponent } from './some/some.component';
@Injectable({
providedIn: 'root'
})
export class SomeDeactivateGuard implements CanDeactivate<SomeComponent> {
canDeactivate(
component: SomeComponent,
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
// 當要離開Component時,會自動呼叫canDeactivate()
return true;
}
}
const routes: Routes = [
{
path: 'some',
component: SomeComponent,
canDeactivate: [SomeDeactivateGuard] // 會去呼叫canActivate(),回傳true就能進去article/tag
},
];
Query String或matrix URL notation只能傳簡單的資料型別
const routes: Routes = [
{
path: 'some/:itemID',
component: SomeComponent,
resolve: { item : ItemLoadResolverService} // 實作Resolver
key Resolver實作
},
];
...
import { ActivatedRoute } from '@angular/router';
import { Item } from '../model/item';
@Component({...})
export class SomeComponent implements OnInit {
public item: Item;
constructor(private route: ActivatedRoute){}
ngOnInit(){
// 訂閱ActivatedRoute上data元素異動
// 排除對DataService的相依
this.route.data.subscribe((data: {item: Item})={
key resolve所回傳的東西
this.item = data.item;
});
}
}
import { Injectable } from '@angular/core';
import { DataService } from './data.service';
import { Resolve, ActivateRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { Item } from '../model/item';
import { Observable } from 'rxjs/Observable';
@Injectable()
export class ItemLoadResolverService implements Resolve<Item>{
constructor(private dataService: DataService){}
resolve(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot):
Item | Observable<Item> | Promise<Item>{
// path: 'some/:itemID' 從URL取得 :itemID
const itemID = route.paramMap.get('itemID');
// 取得值後回傳Observable<Item>
// 排除SomeComponent對DataService的相依
return this.dataService.getItem(itemID);
}
}