Angular4.0基礎知識之組件
Angular4.0基礎知識之路由
Angular4.0依賴注入
Angular4.0數據綁定&管道
路由
簡介
接下來學習路由的相關知識
本來是不准備寫下去的,因為當時看視頻學的時候感覺自己掌握的不錯 ( 這是一個灰常不好的想法 ) ,過了一段時間才發現Angular這個對我這個PHP程序猿來說不太常用的東西非常容易忘!幸好之前去寫了筆記。
首先需要先了解一個概念(SPA),也就是單頁面應用,一個頁面只加載一次,不再刷新,只改變頁面部分內容的應用。
路由的作用就是為每一個視圖分配一個唯一的URL,進入這個URL的時候,使應用跳到某個特定的視圖狀態。
創建
在創建項目的時候 , 帶上參數ng new RouterDemo --routing
即可生成一個帶路由文件的項目
Angular路由常見對象
名稱 | 簡介 |
---|---|
Routes | 路由的配置,URL和組件之間的映射以及組件和組件插座RouterOutlet的映射關系 |
RouterOutlet | 在HTML中標記組件插入位置的占位符標簽 |
Router | 在運行時執行路由的對象,navigate() 和navigateByUrl() 方法導航到指定的路由,使用依賴注入在控制器中獲取 |
RouterLink | 在HTML中聲明路由導航的標簽屬性 |
ActivatedRoute | 當前激活的路由對象,保存着當前路由的信息,如路由地址參數等,使用依賴注入在控制器中獲取 |
在項目中,路由文件通常為app-routing.module.ts
配置
打開路由文件,在routes:Routes對象中定義路由列表,其中,每一個路由至少包含兩個參數,即path
和component
也就是URL和組件的映射關系
注意:這里的
path
最好不要以/
開頭,否則會導致路由URL相對關系的混亂,Angular會自動幫你處理和子路由的關系,除非你明確知道你要做什么
app-routing.module.ts源碼
import {NgModule} from '@angular/core'
import {RouterModule, Routes} from '@angular/router';
import {ProductDetailComponent} from './product-detail/product-detail.component';
import {HomeComponent} from './home/home.component';
const routes: Routes = [
{path: '', component: HomeComponent},
{path: 'product/:prodTitle', component: ProductDetailComponent}
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
providers: []
})
export class AppRoutingModule {}
app.module.ts修改部分
imports: [
...
AppRoutingModule
...
],
如上,最簡單的路由便定義完畢啦
插座
所謂的插座,也就是在HTML中定義的路由對應的組件插入點
使用<router-outlet></router-outlet>
標簽定義路由對應組件的插入位置(在該標簽下面)
路由鏈接
使用<a [routerLink]=['/product']>商品詳情</a>
來定義一個路由導航鏈接
注意這里的路由字符串需要加上
/
,后面我們會使用./
等來區分路由和子路由
路由的參數是一個數組而不是字符串,因為后面我們需要給路由傳遞參數
然后我們就可以通過點擊商品詳情鏈接來顯示product
組件的內容了
使用Router對象進行導航
當你定義了一個事件進行跳轉的時候,例如:
<input type="button" (click)="toProductInfo()" />
控制器代碼
export class AppComponent {
// 使用依賴注入拿到Router對象
constructor(private router:Router){}
// 事件綁定的方法,跳轉
toProductInfo() {
this.router.navigate(['/product']);
}
}
即可實現用代碼進行路由跳轉
默認路由
當輸入一個不存在的地址時,路由插座區域無法顯示,並且會在控制台拋出異常,我們可以通過定義一個默認路由(例如404page)來避免錯誤發生
首先,我們生成一個新的組件,運行ng g component code404
生成404頁面組件,簡單編寫內容之后,進入路由配置文件,添加一個新的路由配置信息
...
// 放在最后,當匹配不到的時候會選擇此路由
{path:'**',component:Code404Component}
傳遞參數
傳遞
傳遞方式 | 形式 | 獲取方式 |
---|---|---|
查詢參數傳遞(GET方法) | <a [routerLink]=['/product'] [queryParams]="{id:1}"></a> =>/?id=1&name=jeffrey |
ActivatedRoute.queryParams['id'] |
在路由形式中定義參數 | {path:'product/:id'}=><a [routerLink]=['/product/',1]></a> =>/product/1 |
ActivatedRoute.params['id'] |
在路由配置中定義靜態數據 | {{path:'product/:id',component:ProductComponent,data:[{osProd:true}]}} | ActivatedRoute.data[0]['isProd'] |
獲取
在constructor構造函數參數中使用依賴注入獲取到ActivatedRoute
存入routeInfo
變量,在ngOnInit()取出參數
- 直接取出(參數快照)
this.productId=this.routeInfo.snapshort.queryParams['id']
- 關聯獲取(參數訂閱),在同翼哥組件之間路由的時候,由於
ngOnInit()
只會執行一次,導致參數不能刷新,這時候可以使用參數訂閱來關聯地獲取到參數。(下面例子使用了箭頭函數)
this.routeInfo.params.subscribe((params:Params)=>this.productId=params['id'])
重定向路由
在訪問一個特定路由時,重定向到另一個指定地址
例如:
{path: '', redirectTo:'/home',pathMatch:'full'},
{path: 'home', component: HomeComponent},
pathMatch
指匹配策略
當我們訪問http://127.0.0.1:4200
的時候,會自動跳轉到http://127.0.0.1:4200/home
子路由
在一個路由的組件中展示其他組件的內容時,使用子路由來實現。
其實更應該理解為“子組件”,也就是一個大的組件里的一部分,使用子路由來控制
在主路由的routerOutlet
顯示主路由的組件內容時,根據子路由的變化,在主路由組件中子路由對應routerOutlet
位置顯示對應的子路由組件
輔助路由
形式:<router-outlet name="fuzhu"></router-outlet>
{path:'xxx',component:XxxComponent,outlet:'fuzhu'}
<a [routerLink]=['/home',{outlets:{fuzhu:'xxx'}}]>鏈接</a>
或<a [routerLink]=[{outlets:{primary:'home',fuzhu:'xxx'}}]>鏈接</a>
當點擊鏈接的時候,主插座會顯示home組件的內容,fuzhu插座會顯示xxx路由匹配到的Xxx組件
<a [routerLink]=[{outlets:{fuzhu:'xxx'}}]>鏈接</a>
當點擊鏈接的時候,主插座不變,fuzhu插座會顯示xxx路由匹配到的Xxx組件
<a [routerLink]=[{outlets:{fuzhu:null}}]>鏈接</a>
當點擊鏈接的時候,fuzhu插座不顯示任何組件
輔助路由允許你在同一個組件中定義多個插座,並定義每個插座顯示的內容
路由守衛
簡介
所謂的路由守衛,也就是在滿足一定條件的時候才允許進入或退出某一個路由。例如:
- 在用戶登錄之前,不允許進入個人中心頁面
- 在某個表單的執行流程中,只有用戶完成了上一步的任務之后,才能進入下一步的環節
- 當用戶沒有執行保存操作而試圖離開某一個路由的時候,阻止離開並進行提示
路由守衛主要有三種類型:
- CanActivate 是否能進入到某個路由
- CanDeactivate 是否能離開某個路由
- Resolve 在路由激活之前獲取數據
現在,在路由對象里我們有多了一個新的參數:canActivate,該參數是數組格式,也就是說,一條路由允許接收多個守衛
那么如何編寫守衛呢?
canActivate守衛
在src
目錄中建立一個存放守衛的目錄guard
,新建路由守衛TypeScript文件,例如login.guard.ts
,下面展示一個簡單的Demo:
(為了便於演示,不去做真正的登錄服務,只是生成一個隨機數來判斷是否已經登錄)
import {canActivate} from "@angular/router";
export class LoginGuard implements CanActivate {
canActivate(){
// 假設隨機數小於0.5就代表已經登錄
let isLogin:boolean = Math.random()<0.5;
if(!isLogin){
console.log("未登錄");
}
return isLogin;
}
}
import {LoginGuard} from "./guard/login.guard";
{path:'product/:id',component:ProductComponent,children:[......],canActivate:[LoginGuard]}
...
@NgModule({
imports:[...],
exports:[...],
providers:[LoginGuard]
})
這樣,我們就是先了一個簡單的路由守衛,當我們試圖導航到這個路由的時候,會判斷守衛返回的Boolean值,為True則通過。
!還有這種操作?!在學Angular的時候順便學了TypeScript~
canDeactivate守衛
同理,我們能很輕易地區是先一個canDeactivate守衛,區別在於canDeactivate守衛在是先接口的時候需要制定一個泛型(也就是需要保護的組件),算了,直接上代碼吧:
import {CanDeactivate} from "@angular/router";
import {ProductComponent} from "../product/";
export class UnsavedGuard implements CanDeactivate<ProductComponent>{
/*
需要實現一個方法
因為是需要離開,那么這里需要根據組件里的某些狀態來判定
*/
canDeactivate(component:ProductComponent){
return window.confirm("您還沒保存,確定要離開嗎?");
}
}
Resolve守衛
Resolve守衛常用於解決數據預加載問題,如果使用路由中傳遞的參數,在進入某一個組件之后發出Http請求去獲取所需要的數據,那么在剛進入這個組件的時候,所有使用插值表達式的位置都是空的,這樣用戶體驗會很差。這時候可以使用Resolve守衛來解決,在進入之前先獲取數據,進入之后立即使用並顯示出來
import {ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot} from '@angular/router';
import {Product} from '../product/product.component';
import {Observable} from 'rxjs/Observable';
import {Injectable} from '@angular/core';
@Injectable() // 裝飾器,允許注入
export class ProductResolve implements Resolve<Product> {
// 注入路由對象
constructor(private router: Router) {}
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Product | Observable<Product> | Promise<Product> {
const productId: number = route.params['id'];
if (productId === 1) {
return new Product(1, '小米6', 2999, 5, '很不錯的手機', ['數碼'])
} else {
this.router.navigate(['/home']);
return undefined;
}
}
}
在路由中我們有遇到了一個新的參數:resolve
,接收一個數組(Resolve守衛)
{path:'product/:id',component:ProductComponent,resolve:{product:ProductResolve}}
同樣需要在providers里聲明一下。
那么如何取出Resolve守衛傳入的數據呢?
同樣可以使用參數訂閱的方式:
// routeInfo:ActivatedRoute
this.routeInfo.data.subscribe((data:{product:Product})=>{
this.productId=data.product.id;
});
好了,路由的知識點到現在就告一段落,但是Angular的學習之路仍未完待續......