iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 25
1
Modern Web

Angular 深入淺出三十天系列 第 25

[Angular 深入淺出三十天] Day 24 - 路由(七)

相信大家都用使用過 Facebook 吧?

當我們想在 Facebook 發文或留言的時候,如果不小心按到上一頁/下一頁,抑或是沒有取消發文或留言的狀態就想離開頁面的時候,應該會看到類似這樣子的訊息:

Imgur

既然我們昨天可以在使用者想要造訪某個路由時,透過 Guard 來替我們的路由把關,那是不是也可以像 Facebook 一樣,在使用者想要離開某個路由時,一樣透過 Guard 來幫我提醒使用者呢?

當然可以!而且使用方式大致上跟昨天很像,我們來快速複習一下。

假設我們今天要在 Login 頁面設上述這種類型的守門員的話,我們一樣可以先使用 Angular CLI 建立一個 Guard :

ng generate guard login/ensure-login

然後打開 ensure-login.guard.ts ,應該也會是長這樣:

import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class EnsureLoginGuard implements CanActivate {
  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
    return true;
  }

到目前為止都是跟昨天一樣,不過我們今天要做的事情跟昨天不一樣,所以需要調整一下程式碼。

首先我們先把 canActivate 函式整個移除,然後將 CanActivate 介面改為 CanDeactivate

export class EnsureLoginGuard implements CanDeactivate<LoginComponent> {
}

CanDeactivate 這個介面有泛型,泛型的型別則是這個 Guard 要我們處理那個路由所設定的 Component 的型別。像我們今天就是要讓它來幫我們處理 LoginComponent 這個 Component 。

當我們的程式碼改成這樣的時候,應該會看到 EnsureLoginGuard 有紅色波浪底線出現:

Imgur

滑鼠游標移過去之後,VSCode 會這樣告訴你:

Imgur

這個意思是 CanDeactivate 需要實作 canDeactivate 這個函式,所以我們其實只要加上去就好了。

不過今天要教大家一個小技巧 (其實早就該教了XD) ,這時候我們只要點選 EnsureLoginGuard 或是將游標停在上面之後,按下 ctrl + '.' (masOS 則是按下 command + '.' , VSCode 就會幫我們把函式加進去:

Imgur

但我覺得 VSCode 幫我們處理得沒那麼好,我們調整一下好了:

export class EnsureLoginGuard implements CanDeactivate<LoginComponent> {

  /**
   * 當使用者要離開這個 Guard 所防守的路由時,會觸發這個函式
   *
   * @param {LoginComponent} component - 該路由的 Component
   * @param {ActivatedRouteSnapshot} currentRoute - 當前的路由
   * @param {RouterStateSnapshot} currentState - 當前路由狀態的快照
   * @param {RouterStateSnapshot} [nextState] - 欲前往路由的路由狀態的快照
   * @returns {(boolean | Observable<boolean> | Promise<boolean>)}
   * @memberof EnsureLoginGuard
   */
  canDeactivate(
    component: LoginComponent,
    currentRoute: ActivatedRouteSnapshot,
    currentState: RouterStateSnapshot,
    nextState?: RouterStateSnapshot
  ): boolean | Observable<boolean> | Promise<boolean> {
    return false;
  }

}

然後我們再打開 app-routing.module.ts 這個檔案,把 EnsureLoginGuard 加到 LoginComponent 的路由裡:

{
  path: 'login',
  component: LoginComponent,
  canDeactivate: [EnsureLoginGuard]
}

有注意到嗎?昨天我們用的是 canActivate ,今天則是改用 canDeactivate

加完之後我們來看看畫面:

Imgur

糟糕!怎麼都沒辦法登入了?!是不是壞了?!

別擔心,這其實是因為我們在 EnsureLoginGuard 的 canDeactivate 函式裡回傳 false 的關係。表示我們不讓使用者離開那個頁面的意思。

我們再來調整一下程式碼,讓使用者要離開該頁面的時候跳個提示:

canDeactivate(
  component: LoginComponent,
  currentRoute: ActivatedRouteSnapshot,
  currentState: RouterStateSnapshot,
  nextState?: RouterStateSnapshot
): boolean | Observable<boolean> | Promise<boolean> {
  
  return confirm('是否要離開此頁面?');
  
}

然後再來看一下畫面:

Imgur

如此一來我們就可以依照使用者自己的意願來決定要不要導頁了。

不過在實務上應用時,通常判斷的邏輯會會再稍微複雜一點點。

舉例來說,假設今天有個情境是:使用者正在打文章或是表單,如果使用者打到一半的時候要離開這個頁面的話,就跳提示訊息讓使用者自己判斷要不要離開;如果使用者都還沒有任何輸入的時候要離開就直接離開。

這時候我們就必須判斷使用者有沒有任何的輸入,再決定要不要跳提示訊息了。

怎麼做呢?

我們可以先在 LoginComponent 的 Template 裡加上 input 的標籤:

<input type="text" [(ngModel)]="name">

因為我們有用 [(ngModel)] 的關係,所以要引入 FromsModule 到我們的 AppModule 裡:

import { FormsModule } from '@angular/forms';
@NgModule({
  declarations: [
    // ...
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    FormsModule // 加到這裡
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

然後我們雙向綁定了 name 這個屬性,所以到 LoginComponent 裡新增一下這個屬性:

name = '';

最後調整一下 EnsureLoginGuard 的程式碼:

canDeactivate(
  component: LoginComponent,
  currentRoute: ActivatedRouteSnapshot,
  currentState: RouterStateSnapshot,
  nextState?: RouterStateSnapshot
): boolean | Observable<boolean> | Promise<boolean> {

  if (component.name.trim()) {
    return confirm('是否要離開此頁面?');
  }

  return true;

}

來看一下改完的效果:

Imgur

好的!如此一來就完成了第二種路由守門員了!!

是不是超簡單又超輕鬆的阿?!

這七天的範例程式碼我會放在 GitHub 上,有興趣的邦友們都可以自由下載與運用噢!


經過了漫長的七天之後,關於路由的部份也終於可以告一個段落了。

明天會幫大家總結一下這七天所學到的東西,敬請期待!


上一篇
[Angular 深入淺出三十天] Day 23 - 路由(六)
下一篇
[Angular 深入淺出三十天] Day 25 - 路由總結(一)
系列文
Angular 深入淺出三十天33

尚未有邦友留言

立即登入留言