angular是單頁應用需要配置路由來實作頁面跳轉。
路由的例子
通過超連結導航
建立一個項目,
ng new router --routing
然後用webstorm打開項目
生成項目時添加了–routing參數後,
會多生成一個app-routing.module.ts檔案
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
const routes: Routes = [
{
path:'',
children: []
}
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
providers:[]
})
export class AppRoutingModule { }
在app.module.ts中也會多引入AppRoutingModule
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import {FormsModule} from '@angular/forms';
import {HttpModule} from '@angular/http';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
FormsModule,
HttpModule,
AppRoutingModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
在app.component.html中會加入router-outlet
<h1>
{{title}}
</h1>
<router-outlet></router-outlet>
新生成兩個元件ng g component home,ng g component product
然後修改app-routing.modules.ts,為了保證通用性,這裡寫位址的時候不要在前面加上/
const routes: Routes = [
{path:'',component:HomeComponent},
{path:'product',component:ProductComponent}
];
修改app.component.html
<a [routerLink]="['/']">首頁</a>
<a [routerLink]="['/product']">商品詳情</a>
<router-outlet></router-outlet>
當我通路localhost:4200的時候顯示的是home works!,當我點選商品詳情,會顯示product works!并且位址變成了localhost:4200/product
通過觸發事件導航
修改app.component.html
<a [routerLink]="['/']">首頁</a>
<a [routerLink]="['/product']">商品詳情</a>
<input type="button" value="商品詳情" (click)="toProductDetails()">
<router-outlet></router-outlet>
import { Component } from '@angular/core';
import {Router} from "@angular/router";
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'app';
constructor(private router: Router){}
toProductDetails(){
this.router.navigate(['/product']);
}
}
運作程式,點選按鈕,下邊就會顯示product works!
配置404頁面路由
新生成一個元件ng g component code404
修改app-routing.module.ts
const routes: Routes = [
{path:'',component:HomeComponent},
{path:'product',component:ProductComponent},
{path:'**',component:Code404Component}
];
當通路一個不存在的路徑時會顯示code404 works!
顯示路由是從上往下比對位址,如果把{path:’**’,component:Code404Component}放到前面不管通路那個頁面都會顯示code404了。是以具有通用性的路由要放到最後邊。
在路由中傳遞資料
在查詢參數中傳遞資料
/product?id=&name= => ActivatedRoute.queryParams[id]
在路由路徑中傳遞資料
{path:/product/:id} =>/product/ => ActivatedRoute.queryParams[id]
在路由配置中傳遞資料
{path:/product,component:ProductComponent,date[{isProd:true}]}
=> ActivatedRoute.data[0][isProd]
在查詢參數中傳遞資料
修改app.component.html
這樣點選這個連接配接的時候位址就會為http://localhost:4200/product?id=1
修改product.component.ts
import {Component, OnInit} from '@angular/core';
import {ActivatedRoute} from "@angular/router";
@Component({
selector: 'app-product',
templateUrl: './product.component.html',
styleUrls: ['./product.component.css']
})
export class ProductComponent implements OnInit {
private productId: number;
constructor(private routeInfo: ActivatedRoute) {
}
ngOnInit() {
this.productId = this.routeInfo.snapshot.queryParams["id"];
}
}
修改product.component.html
<p>
這裡是商品資訊元件
</p>
<p>
商品ID是:{{productId}}
</p>
在路由路徑中傳遞資料
修改app-routing.module.ts
{path:'product/:id',component:ProductComponent},
修改app.component.html
修改product.component.ts
參數快照和參數訂閱
修改app.component.ts,加了一個參數2
this.router.navigate(['/product',]);
啟動程式。我點選了首頁連結後再點商品詳情連結。我點選首頁連結,然後點選商品詳情按鈕。都很正常,連結和下面顯示的商品id都對應的上。
但是,當我點選商品詳情連結之後,再點選商品詳情按鈕。位址欄由1變成了2,但下邊顯示的商品id卻還是1。
從home元件到product元件會重新建立product元件重新調用ngOnInit方法。
從product元件到product元件,不會重新建立product元件,是以不會重新調用ngOnInt方法。
之前我們使用的都是snapshot,參數快照。
現在我們用參數訂閱,subscribe,來解決這個問題
修改product.component.ts
this.routeInfo.params.subscribe((params: Params)=>this.productId=params["id"]);
弄了一個匿名函數把傳進來的params參數指派給productId
這樣做了之後就沒有之前的問題了。
雖然商品詳情元件沒有被建立,但是參數變了,而我訂閱了那個參數,每次我都能接受到改變了的那個值,然後把這個值指派為productId。
在以後寫代碼中如果确定不會從自身路由到自身就可以用參數快照的方式。
如果不确定就要用參數訂閱的方式來擷取參數。
重定向路由
在使用者通路一個特定的位址時,将其重定向到另一個指定的位址。
修改app-routing.module.ts
const routes: Routes = [
{path:'',redirectTo:'/home',pathMatch:'full'},
{path:'home',component:HomeComponent},
{path:'product/:id',component:ProductComponent},
{path:'**',component:Code404Component}
];
當我通路localhost:4200的時候會重定向到home
修改app.component.html
子路由
{path:'home',component:HomeComponent,
children:[
{
path:'',component:XxxComponent
},
{
path:'/yyy',component:YyyComponent
}
]}
建立商品描述元件ng g component productDesc
建立銷售員資訊元件ng g component sellerInfo
修改product-desc.component.html
<p>
這是一個牛X的商品!
</p>
修改seller-info.component.html
<p>
銷售員ID是:{{sellerId}}
</p>
修改seller-info.component.ts
import { Component, OnInit } from '@angular/core';
import {ActivatedRoute} from '@angular/router';
@Component({
selector: 'app-seller-info',
templateUrl: './seller-info.component.html',
styleUrls: ['./seller-info.component.css']
})
export class SellerInfoComponent implements OnInit {
private sellerId:number;
constructor(private routeInfo:ActivatedRoute) { }
ngOnInit() {
this.sellerId=this.routeInfo.snapshot.params["id"];
}
}
修改app-routing.module.ts
{path:'product/:id',component:ProductComponent,
children:[
{path:'',component:ProductDescComponent},
{path:'seller/:id',component:SellerInfoComponent}
]},
修改product.component.html
<p>
這裡是商品資訊元件
</p>
<p>
商品ID是:{{productId}}
</p>
<a [routerLink]="['./']">商品描述</a>
<a [routerLink]="['./seller',99]">銷售員資訊</a>
<router-outlet></router-outlet>
/是從跟路由開始找,./是從目前路由開始找。
路由資訊都是在子產品層的,元件本身不知道關于路由的資訊。
輔助路由
<router-outlet></router-outlet>
<router-outlet name="aux"></router-outlet>
{path:'xxx',component:XxxComponent,outlet:"aux"}
{path:'yyy',component:YyyComponent,outlet:"aux"}
<a [routerLink]="['/home',{outlets:{aux:'xxx'}}]">Xxx</a>
<a [routerLink]="['/product',{outlets:{aux:'yyy'}}]">Yyy</a>
以前一個元件中隻可以定義一個插座,而現在可以定義多個插座。
輔助路由案例整體思路
- 在app元件的目标上再定義一個插座來顯示聊天面闆
- 單獨開發一個聊天室元件,隻顯示在新定義的插座上
- 通過路由參數控制新插座是否顯示聊天面闆
修改app.component.html,添加輔助路由
新建立一個元件ng g component chat
修改chat.component.html
修改chat.component.css
.chat{
background:green;
height:px;
width:%;
float: left;
box-sizing:border-box;
}
修改product.component.html
<div class="product">
<p>
這裡是商品資訊元件
</p>
<p>
商品ID是:{{productId}}
</p>
<a [routerLink]="['./']">商品描述</a>
<a [routerLink]="['./seller',99]">銷售員資訊</a>
<router-outlet></router-outlet>
</div>
修改product.component.css
.product{
background:yellow;
height:px;
width:%;
float: left;
box-sizing:border-box;
}
修改home.component.html
<div class="home">
<p>
這裡是首頁元件
</p>
</div>
修改home.component.css
.home{
background:red;
height:px;
width:%;
float: left;
box-sizing:border-box;
}
修改app-routing.module.ts
{path:'',redirectTo:'/home',pathMatch:'full'},
{path:'chat',component:ChatComponent,outlet:'aux'},
修改app.component.html
<a [routerLink]="['/home']">首頁</a>
<a [routerLink]="['/product',1]">商品詳情</a>
<input type="button" value="商品詳情" (click)="toProductDetails()">
<a [routerLink]="[{outlets:{aux:'chat'}}]">開始聊天</a>
<a [routerLink]="[{outlets:{aux:null}}]">結束聊天</a>
<router-outlet></router-outlet>
<router-outlet name="aux"></router-outlet>
修改app.component.html
當我點選開始聊天時,主路由會顯示為home。
路由守衛
應用場景:
- 隻有當使用者已經登入并擁有某些權限時才能進入某些路由。
- 一個由多個表單元件組成的向導,例如注冊流程,使用者隻有在目前路由的元件中填寫了滿足要求的資訊才可以導航到下一個路由
- 當使用者未執行儲存操作而試圖離開目前導航時提醒使用者
三種路由守衛
- CanActivate:處理導航到某路由的情況
- CanDeactivate:處理從目前路由離開的情況
- Resolve:在路由激活之前擷取路由資料
CanActivate守衛
app,右鍵,建立一個目錄,名字為guard,右鍵guard,建立一個TypeScript檔案,名字為login.guard.ts,填寫代碼
import {CanActivate} from "@angular/router";
export class LoginGuard implements CanActivate {
canActivate() {
let loggedIn: boolean = Math.random() < ;
if (!loggedIn) {
console.log("使用者未登陸");
}
return loggedIn;
}
}
修改app-routing.module.ts
{path:'product/:id',component:ProductComponent,children:[
{path:'',component:ProductDescComponent},
{path:'seller/:id',component:SellerInfoComponent}
],canActivate:[LoginGuard]},
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
providers:[LoginGuard]
})
CanDeactivate守衛
右鍵guard,建立一個TypeScript檔案,名字為unsave.guard.ts
填寫代碼
import {ProductComponent} from "../product/product.component";
import {CanDeactivate} from "@angular/router";
export class UnsavedGuard implements CanDeactivate<ProductComponent>{
canDeactivate(component: ProductComponent){
return window.confirm("你還沒有儲存,确定要離開麼?");
}
}
修改app-routing.module.ts
{path:'product/:id',component:ProductComponent,children:[
{path:'',component:ProductDescComponent},
{path:'seller/:id',component:SellerInfoComponent}
],canActivate:[LoginGuard]
,canDeactivate:[UnsavedGuard]},
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
providers:[LoginGuard,UnsavedGuard]
})
resolve守衛
解決的問題,在顯示頁面之前,頁面中的資料顯示的時候可能有延遲,這樣顯示資料的地方會都顯示空。
這個守衛的作用就是,在顯示頁面之前預先到伺服器去讀資料
右鍵guard,建立一個TypeScript檔案,名字為product.resolve.ts
import {Resolve,ActivatedRouteSnapshot,RouterStateSnapshot,Router} from "@angular/router";
import {Product} from "../product/product.component";
import {Observable} from "rxjs";
import {Injectable} from "@angular/core";
@Injectable()
export class ProductResolve implements Resolve<Product>{
constructor(private router:Router){}
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Product> | Promise<Product> | Product {
let productId:number=route.params["id"];
if(productId==){
return new Product(,"iPhone7")
}else{
this.router.navigate(['/home']);
return undefined;
}
}
}
修改product.component.ts
import {Component, OnInit} from '@angular/core';
import {ActivatedRoute,Params} from "@angular/router";
@Component({
selector: 'app-product',
templateUrl: './product.component.html',
styleUrls: ['./product.component.css']
})
export class ProductComponent implements OnInit {
private productId: number;
private productName:string;
constructor(private routeInfo: ActivatedRoute) {
}
ngOnInit() {
this.routeInfo.params.subscribe((params: Params)=>this.productId=params["id"]);
this.routeInfo.data.subscribe((data:{product:Product})=>{
this.productId=data.product.id;
this.productName=data.product.name
});
}
}
export class Product{
constructor(public id:number,public name:string){}
}
修改product.component.html
<div class="product">
<p>
這裡是商品資訊元件
</p>
<p>
商品ID是:{{productId}}
</p>
<p>
商品名稱是:{{productName}}
</p>
<a [routerLink]="['./']">商品描述</a>
<a [routerLink]="['./seller',99]">銷售員資訊</a>
<router-outlet></router-outlet>
</div>
改造線上競拍應用
步驟:
- 建立商品詳情元件,顯示商品的圖檔和标題
- 重構代碼,把輪播圖元件和商品清單元件封裝到新的Home元件
- 配置路由,在導航到商品詳情元件時傳遞商品的标題參數
- 修改App元件,根據路由顯示Home元件或商品詳情元件
- 修改商品清單元件,給商品标題添加帶routLink指令的連結,導航到商品詳情路由
建立元件ng g component productDetail
修改product-detail.component.ts
import { Component, OnInit } from '@angular/core';
import{ActivatedRoute} from '@angular/router';
@Component({
selector: 'app-product-detail',
templateUrl: './product-detail.component.html',
styleUrls: ['./product-detail.component.css']
})
export class ProductDetailComponent implements OnInit {
productTitle:string;
constructor(private routeInfo:ActivatedRoute) { }
ngOnInit() {
this.productTitle=this.routeInfo.snapshot.params["prodTitle"];
}
}
修改product-detail.component.html
<div>
<img src="http://placehold.it/820x230">
<h4>{{productTitle}}</h4>
</div>
建立home元件,ng g component home
把app.component.css的内容剪切到home.component.css
把app.component.html中的以下内容複制到home.component.html
<div class="row carousel-container">
<app-carousel></app-carousel>
</div>
<div class="row">
<app-product></app-product>
</div>
并添加
到原來的位置
修改app.module.ts
const routeConfig:Routes=[
{path:'',component:HomeComponent},
{path:'product/:prodTitle',component:ProductDetailComponent}
]
imports: [
BrowserModule,
RouterModule.forRoot(routeConfig)
],
修改product.component.html,增加routerLink