1. 程式人生 > >angular4、angular4.0從入門到實戰 打造股票管理網站 1.子路由 2. 路由傳參三種方式 3. 路由守衛

angular4、angular4.0從入門到實戰 打造股票管理網站 1.子路由 2. 路由傳參三種方式 3. 路由守衛

可能有點語無倫次。。。。。因為第一次寫這麼長的博文~~~

最近跳入了angular4的坑,之前看的angular權威教程是angularJS1.的版本,之後的angular2和angular4改動非常大,可以說是兩個框架了(雷鋒和雷峰塔),google公司只對angularJS1.維護兩年,所以果斷棄了angularJS,現在大多是angular2和angular4應用居多 錘子科技官網http://www.smartisan.com/#/shop就是用angular4寫的,現在學到了路由,跟著視訊過了一遍,然後又自己寫一遍程式碼加深記憶,視訊是慕課網出的angular4.0從入門到實戰 打造股票管理網站

有需要的小夥伴可以留言 路由這一章課程有三個小例子,spa (single page application)單頁面應用,路由真的炒雞有用啊感覺, 才接觸angular我想記錄下來因為忘的炒雞快啊衰,環境搭建的時候有各種錯誤 搞得心累 要裝node,angularCli npm 環境搭建這裡有問題也可以留言因為我真的碰到好多問題!!!! 我用的是webstorm2017.2 這個博文內容比較雜,有時間分開一下 這樣不好找,內容大概有

  1. 子路由
  2. 路由傳參三種方式
  3. 路由守衛

這是最終的目錄截圖

第一個超簡單路由應用

說明:點選主頁顯示的是home元件的內容,商品詳情是product元件的內容,
我會放截圖還有需要注意的地方
先說一下流程(環境搭建就不說了,網上有很多教程博文)
1.建立專案 ng new myrouter –routing
/———後面的–routing必須加上,才會生成路由配置檔案,我下面那個圖裡可以看到app-routing-modules.ts———

/
2.npm install jquery –save (兩個橫線!)
npm install bootstrap –save
npm install @types/jquery –save-dev
npm install @types/bootstrap –save-dev
(後面這兩個也要裝上,因為angular是typescript寫的,而這兩個庫
javascript~~大概是因為這個)
3.接下來就是新增你要用的元件了
ng g component home
ng g component product
ng g component code404
4.準備工作都做好了,現在開始碼程式碼~
app.component.html
這是app.component.html裡的內容,<router-outlet>標籤必須有,這相當於插座,讓待顯示的元件的內容知道放到哪裡,

home.component.html

  <div class="home">
  <p>
    這裡是主頁元件
  </p>

</div>

product.component.html

<div class="product">
  <p>
    這裡是商品資訊元件
  </p>
  <p>
    商品ID是
  </p>
  <p>
    商品ID是
  </p>

</div>

元件裡的內容可以自由構思,接下來就是路由配置
在app-routing.modules.ts裡配置,只需要修改Routes裡內容即可,

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import {HomeComponent} from './home/home.component';
import {ProductComponent} from './product/product.component';
import {Code404Component} from './code404/code404.component';

const routes: Routes = [
    {path: 'home', component: HomeComponent},
    {path: 'product', component: ProductComponent},
    {path: '', redirectTo: '/home', pathMatch: 'full'},
    {path: '**', component: Code404Component}
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }
{path: '', redirectTo: '/home', pathMatch: 'full'},

這個意思就是一進入頁面就跳轉顯示指定的home元件的內容,這個可以自己指定,path不能以斜槓(/)開頭。 路由器會為解析和構建最終的URL,這樣當我們在應用的多個檢視之間導航時,可以任意使用相對路徑和絕對路徑。路由的定義順序是刻意如此設計的。路由器使用先匹配者優先的策略來匹配路由,所以,具體路由應該放在通用路由的前面。在上面的配置中,帶靜態路徑的路由被放在了前面,後面是空路徑路由,因此它會作為預設路由。而萬用字元路由被放在最後面,這是因為它能匹配上每一個URL,因此應該只有在前面找不到其它能匹配的路由時才匹配它。

 {path: 'product', component: ProductComponent},
    {path: '**', component: Code404Component}

這兩行程式碼,第一行是常規指定路徑的方法,一般在輸入ProductComponent時會自動在檔案前面新增import{…},alt+enter就可以新增 我下載的webstorm沒有自己配置,如果設定快捷鍵了就按自己的,,第二行path路徑是萬用字元,意思就是如果使用者輸入的路徑不存在就會顯示指定的元件Code404Component

import {HomeComponent} from './home/home.component';
import {ProductComponent} from './product/product.component';
import {Code404Component} from './code404/code404.component';

子路由、和三種給路由傳遞引數的方法

要求:在商品資訊元件設定點選“顯示商品描述”,
首先新增一個 product-desc元件,編輯對應的html檔案,想讓這個元件在product元件下顯示,所以正題來了。。。。
1. product.html

<div class="product">
  <p>
    這裡是商品資訊元件
  </p>
  <a [routerLink]="['./desc',2]">點選檢視商品詳情</a>
</div>

<router-outlet></router-outlet>

標籤必須寫,routerLink是一個數組,後面的數字是傳入的引數,子路由是./
2.配置路由,如果要傳入引數記得格式path:’desc/:id’ id是自定義的
{path: 'product', component: ProductComponent, children: [
{path: 'desc/:id', component: ProductDescComponent}
]},

3.到這一步已經實現了子路由,對於傳進去的引數可以獲取到
因為是給子路由傳遞的引數,所以在product-desc.component.html

<p>這裡是商品描述</p>
<p> 商品ID是{{productId}}</p>

product-desc.component.ts

import { Component, OnInit } from '@angular/core';
import {ActivatedRoute} from '@angular/router';

@Component({
  selector: 'app-product-desc',
  templateUrl: './product-desc.component.html',
  styleUrls: ['./product-desc.component.css']
})
export class ProductDescComponent implements OnInit {

  public  productId: number;
  constructor(private routerInfo: ActivatedRoute) { }

  ngOnInit() {
    this.productId = this.routerInfo.snapshot.params['id'];
  }
}

這樣就可以在路由到product-desc時頁面上就會顯示productId
傳遞引數的方法有三種,一種是上面提到的在路徑中傳遞,還有一種是在查詢引數中傳遞 程式碼如下
product.component.html

 <a [routerLink]= "['./desc']" [queryParams] = "{id:1}"> 點選檢視商品詳情</a>

配置路徑這裡:

 {path: 'product', component: ProductComponent, children: [
      {path: 'desc', component: ProductDescComponent}
    ]},

product-desc.component.html

 ngOnInit() {
    this.productId = this.routerInfo.snapshot.queryParams['id'];
  }

第三種方式:在路由配置中新增靜態資料傳遞引數
路由配置:
{path: 'product', component: ProductComponent, children: [
{path: 'desc', component: ProductDescComponent, data: [{'id': 1}]}
]},

獲取 :data[0][‘id’]表示獲取路由引數中第一個物件的id屬性

this.productId = this.routerInfo.snapshot.data[0]['id'];

product.component.html

  <a [routerLink]= "['./desc']"> 點選檢視商品詳情</a>

路由守衛

這一章剩下的內容就是路由守衛,
看一下angular4中文社群對路由守衛的描述

上面的例子,使用者都能在任何時候導航到任何地方。 但有時候這樣是不對的。

  • 該使用者可能無權導航到目標元件。
  • 可能使用者得先登入(認證)。
  • 在顯示目標元件前,我們可能得先獲取某些資料。
  • 在離開元件前,我們可能要先儲存修改。
  • 我們可能要詢問使用者:你是否要放棄本次更改,而不用儲存它們?

我們可以往路由配置中新增守衛,來處理這些場景。守衛返回一個值,以控制路由器的行為:

如果它返回true,導航過程會繼續
如果它返回false,導航過程會終止,且使用者會留在原地。

守衛還可以告訴路由器導航到別處,這樣也取消當前的導航。
守衛可以用同步的方式返回一個布林值。但在很多情況下,守衛無法用同步的方式給出答案。 守衛可能會向用戶問一個問題、把更改儲存到伺服器,或者獲取新資料,而這些都是非同步操作。

因此,路由的守衛可以返回一個Observable或Promise,並且路由器會等待這個可觀察物件被解析為true或false。

路由器支援多種守衛:

  • 用CanActivate來處理導航到某路由的情況。
  • 用CanActivateChild處理導航到子路由的情況
  • 用CanDeactivate來處理從當前路由離開的情況。
  • 用Resolve在路由啟用之前獲取路由資料。
  • 用CanLoad來處理非同步導航到某特性模組的情況。

在分層路由的每個級別上,我們都可以設定多個守衛。 路由器會先按照從最深的子路由由下往上檢查的順序來檢查CanDeactivate()和CanActivateChild()守衛。 然後它會按照從上到下的順序檢查CanActivate()和CanActivateChild()守衛。 如果特性模組是非同步載入的,在載入它之前還會檢查CanLoad()守衛。 如果任何一個守衛返回false,其它尚未完成的守衛會被取消,這樣整個導航就被取消了。

這一章介紹三個路由守衛

1.canactivate:要求認證
應用程式通常會根據訪問者來決定是否授予某個特性區的訪問權。 我們可以只對已認證過的使用者或具有特定角色的使用者授予訪問權,還可以阻止或限制使用者訪問權,直到使用者賬戶啟用為止。
CanActivate守衛是一個管理這些導航類業務規則的工具。
CanActivate:[] 接收一個數組,可以指定多個路由守衛,當檢視進入此路由時,所有守衛會被依次呼叫,如果有一個守衛返回false,則路由請求會被拒絕掉,看一下步驟
a.建立一個directive資料夾名為guard
b.在此資料夾下建一個typescript檔案,命名為login.guard.ts
c.編寫login.guard.ts

import {CanActivate} from '@angular/router';

export class LoginGuard implements  CanActivate {
  canActivate() {
    let loggedIn : boolean = Math.random() < 0.5;
    if (!loggedIn) {
      console.log( ' 使用者未登入' );
    }
    return  loggedIn;
  }
}

d.配置路由,可以新增多個守衛

 {path: 'home', component: HomeComponent, canActivate: [LoginGuard]},

e.路由配置檔案裡要新增providers
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
providers: [LoginGuard]
})

我測試了一下,對一個元件新增多個守衛,給上一個home元件按照同樣的步驟新增一個test.ts ,讓這個守衛返回false,果然home這個元件就沒法加載出來
test.ts

import {CanActivate} from '@angular/router';

export class TestGuard implements  CanActivate {
    canActivate () {
      return false;
    }
}

2.來說下CanDeactivate 處理未儲存的更改
在現實世界中,我們得先把使用者的改動積累起來。 我們可能不得不進行跨欄位的校驗,可能要找伺服器進行校驗,可能得把這些改動儲存成一種待定狀態,直到使用者或者把這些改動作為一組進行確認或撤銷所有改動。

當用戶要導航到外面時,該怎麼處理這些既沒有稽核通過又沒有儲存過的改動呢? 我們不能馬上離開,不在乎丟失這些改動的風險,那顯然是一種糟糕的使用者體驗。

我們應該暫停,並讓使用者決定該怎麼做。如果使用者選擇了取消,我們就留下來,並允許更多改動。如果使用者選擇了確認,那就進行儲存。

在儲存成功之前,我們還可以繼續推遲導航。如果我們讓使用者立即移到下一個介面,而儲存卻失敗了(可能因為資料不符合有效性規則),我們就會丟失該錯誤的上下文環境。

在等待伺服器的答覆時,我們沒法阻塞它 —— 這在瀏覽器中是不可能的。 我們只能用非同步的方式在等待伺服器答覆之前先停止導航。

我們需要CanDeactivate守衛。
步驟與canactivate一樣,這裡就不再贅述了直接上程式碼
1.unsave.guard.ts

import {CanDeactivate} from '@angular/router';
import {HomeComponent} from "../home/home.component";

export class UnsaveGuard implements CanDeactivate <HomeComponent> {
  canDeactivate (component: HomeComponent) {
         return window.confirm('你還沒有儲存,確定離開嗎?');
  }

2.路徑配置

    {path: 'home', component: HomeComponent, canActivate: [LoginGuard, TestGuard], canDeactivate: [UnsaveGuard]},


@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
  providers: [LoginGuard, TestGuard, UnsaveGuard],
})

當離開home時瀏覽器會彈出訊息框 如果確認就會路由到其他元件,如果取消就會留在當前
3.resolve守衛
預先獲取元件資料,導航前預先載入路由資訊
1.在guard資料夾裡新增檔案product.resolve.ts

import {ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot} from '@angular/router';
import {Injectable} from '@angular/core';
import {Product} from '../product/product.component';
@Injectable ()
export class  ProductResolve implements  Resolve <Product> {
  constructor(private  router: Router) {
  }
  resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
    const productId: number = route.params['id'];
    if (productId == 1) {
      return new Product(2);
    }else {
      this.router.navigate(['/home']);
      return undefined;
    }
  }
}

2.product.component.ts裡實例化
export class Product {
constructor(public productIds: number) {
}
}

3.路徑配置
{path: 'product/:id', component: ProductComponent, children: [
{path: 'desc', data: [{id: 1}], component: ProductDescComponent}
], resolve: [ProductResolve]},

4.providers裡注入

providers: [LoginGuard, TestGuard, UnsaveGuard, ProductResolve],

5.product.component.html

<div class="product">
  <p>
    這裡是商品資訊元件
  </p>
  <a [routerLink]= "['./desc']"> 點選檢視商品詳情</a>
  {{productIds}}
</div>

<router-outlet></router-outlet>




這篇博文到這裡就完了,內容大概就這麼多,也不是很全面,可以去中文社群看看,還有對應的例子可以練手,一邊敲一邊回憶,發現自己忘光了,然後又查文件看視訊,總算是寫完了,過一遍果然是有效果的哈哈哈哈哈~~~~~ 腦袋有點糊,如果哪裡不正確還希望能指正,