因為會忘,所以要寫下來

Angular 路由守衛(登入篇)

經過了昨天的介紹,今天就來看看使用登入範例囉

今天的登入資料依然是使用 FakeStoreAPI

登入畫面

這裡就做個簡單的驗証,沒填值就不能按按鈕

<form [formGroup]="form" (ngSubmit)="onSubmit()">
  <div>
    <label for="">userName:</label>
    <input type="text" formControlName="username" required />
  </div>
  <div>
    <label for="">passWord:</label>
    <input type="password" formControlName="password" required />
  </div>
  <button [disabled]="!form.valid">Login</button>
</form>
import { Component } from "@angular/core";
import { FormControl, FormGroup } from "@angular/forms";
import { Router } from "@angular/router";
import { AuthService } from "../auth.service";

@Component({
  selector: "app-login",
  templateUrl: "./login.component.html",
  styleUrls: ["./login.component.css"],
})
export class LoginComponent {
  form: FormGroup = new FormGroup({
    username: new FormControl(""),
    password: new FormControl(""),
  });

  constructor(private router: Router, private authService: AuthService) {}

  onSubmit() {
    const { username, password } = this.form.value;
    this.authService.login(username, password).subscribe((res: any) => {
      if (res.token) {
        // 登入成功後的跳轉網址
        this.router.navigate([""]);
      } else {
        console.log("error", res);
      }
    });
  }
}

API Service

import { HttpClient, HttpHeaders } from "@angular/common/http";
import { Injectable } from "@angular/core";
import { BehaviorSubject } from "rxjs";
import { tap } from "rxjs/operators";

@Injectable({
  providedIn: "root",
})
export class AuthService {
  setToken$: BehaviorSubject<string> = new BehaviorSubject("");
  constructor(private httpClient: HttpClient) {}
  login(username: string, password: string) {
    const url = `https://fakestoreapi.com/auth/login`;

    return this.httpClient
      .post(
        url,
        JSON.stringify({
          username,
          password,
        }),
        {
          headers: new HttpHeaders({
            "Content-Type": "application/json",
          }),
        }
      )
      .pipe(
        tap((res: any) => {
          this.setToken$.next(res.token);
        })
      );
  }
}

若是有成功得到 token 就裝這狀態記錄在 setToken$


路由守衛 auth.guard.ts

import { Injectable } from "@angular/core";
import {
  CanActivate,
  ActivatedRouteSnapshot,
  RouterStateSnapshot,
  Router,
} from "@angular/router";
import { map, Observable } from "rxjs";
import { AuthService } from "./auth.service";

@Injectable({
  providedIn: "root",
})
export class AuthGuard implements CanActivate {
  constructor(private router: Router, public authService: AuthService) {}

  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): Observable<boolean> | Promise<boolean> | boolean {
    return this.canEnter();
  }

  canActivateChild(): Observable<boolean> {
    return this.canEnter();
  }

  canEnter(): Observable<boolean> {
    // 這裡提取剛剛記錄的狀態
    return this.authService.setToken$.pipe(
      map((isLogin) => {
        if (!isLogin) {
          this.router.navigate(["/", "login"]);
        }
        return !!isLogin;
      })
    );
  }
}

canEnter 訂閱剛剛的 setToken$ 狀態,若是沒有 token 則導回 login 頁


路由設定

import { NgModule } from "@angular/core";
import { RouterModule, Routes } from "@angular/router";
import { LoginComponent } from "./login/login.component";
import { AuthGuard } from "./auth.guard";
import { HomeComponent } from "./home/home.component";

const routes: Routes = [
  {
    path: "login",
    component: LoginComponent,
  },
  {
    path: "",
    component: HomeComponent,
    canActivate: [AuthGuard], //新增路由守衛
  },
  {
    path: "",
    pathMatch: "full",
    redirectTo: "/",
  },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
})
export class AppRoutingModule {}

今天的範例就放在這裡囉 https://stackblitz.com/edit/angular-ivy-pwkhc4


對這篇文章有什麼想法嗎?

Copyright © 2022 Mandy. All rights reserved. Build with Angular