简体   繁体   中英

CanActivate, Cant map to subject

My group and I are building a website using angular for the client side. We're trying to use AuthGuard to lockdown the routes but are having trouble not being able to map to the subject.

LoginService:

import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { Inject } from '@angular/core';
import { Injectable } from '@angular/core';
import { Subject, throwError } from 'rxjs';
import { catchError, reduce, tap } from 'rxjs/operators';
import { AuthResponceData, LoginCredentials } from '../Objects/login.component';
import { CurntUser } from "../Objects/CurntUser.model"

@Injectable({
  providedIn: 'root'
})
export class LoginService {
  userSubject = new Subject<CurntUser>();

  constructor(private http: HttpClient, @Inject('BASE_URL') private baseUrl: string) { }

  public Login(loginCredentials: LoginCredentials) {
    return this.http.post<AuthResponceData>(
      this.baseUrl + "Login",
      loginCredentials
    )
      .pipe(catchError(this.HandleError), tap( resData => {
        const expirIn = new Date( new Date().getTime() + 14400 * 1000);
        const currentUser = new CurntUser(resData.isValid, resData.token, expirIn);
        this.userSubject.next(currentUser);
      }));
  }

  private HandleError(errorResp: HttpErrorResponse) {
    let errorMessage = 'An unknown error occurred.';
    return throwError(errorMessage);
  }
}

This page gets the response back from the server if the users creds were valid, if so creates the current user obj and nexts the subject user using the current user.

AuthGuard:

import { Injectable } from "@angular/core";
import { Router } from "@angular/router";
import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot, UrlTree } from "@angular/router";
import { Observable } from "rxjs";
import { map } from "rxjs/operators"
import { LoginService } from "./login.service"

@Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivate{

    constructor(private loginService: LoginService, private router: Router){}

    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean | UrlTree | Observable<boolean | UrlTree> | Promise<boolean | UrlTree> {
            return this.loginService.userSubject.pipe(map( user => {return !!user || this.router.createUrlTree(['/login']); }));
        }
}

When a link is clicked the CanActivate function is triggered and is supposed to return true or false or redirect to the login page.

Here inlies my issue, return this.loginService.userSubject.pipe(map( user => {return..user || this;router;createUrlTree(['/login']); })); for some reason this line of code fails out. I attempted to follow map down the rabbit whole and at some point it quits and jumps to the end.

Any thoughts/advice/solutions would be greatly appreciated.

You didn't mention where you've called login function of LoginService but I think the problem is this.userSubject.next(currentUser) invoked before subscribing to it. So canActivate is waiting for a new value but receives nothing.

One approach is to hold the status of logged-in user and based on that return a boolean or a url tree or even an observable(if user not loaded yet and you are waiting for http response)

  canActivate(next: ActivatedRouteSnapshot,
              state: RouterStateSnapshot): Observable<boolean | UrlTree> | boolean | UrlTree {
    if (this.userService.userLoaded) {
      return !!this.userService.user ? true : this.router.parseUrl('/');
    }
    return this.userService.userLoaded$.pipe(
      map(data => {
        return !!data ? true : this.router.parseUrl('/');
      })
    );
  }

also you can use a BehaviorSubject instead and decide on last value of it.

One more thing which is not related to your problem. Instead of using a public subject directly, it is better to have a private Subject(which you will call it's next within your service) and a public observable from your Subject like below:

  private userLoadedSubject = new Subject<User>();
  userLoaded$ = this.userLoadedSubject.asObservable();  

In this way you prevent your data to be accidentally changed from other components or services

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM