简体   繁体   English

CanActivate,Cant map 主题

[英]CanActivate, Cant map to subject

My group and I are building a website using angular for the client side.我和我的团队正在为客户端使用 angular 构建一个网站。 We're trying to use AuthGuard to lockdown the routes but are having trouble not being able to map to the subject.我们正在尝试使用 AuthGuard 来锁定路由,但无法通过 map 访问该主题。

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.如果用户信用有效,此页面将从服务器返回响应,如果是这样,则创建当前用户 obj 并使用当前用户下一个主题用户。

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.单击链接时,将触发 CanActivate function 并返回 true 或 false 或重定向到登录页面。

Here inlies my issue, return this.loginService.userSubject.pipe(map( user => {return..user || this;router;createUrlTree(['/login']); }));这就是我的问题, 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.我试图跟随 map 整个兔子,在某个时候它退出并跳到最后。

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.您没有提到您在哪里调用 LoginService 的登录 function 但我认为问题是在订阅它之前调用了this.userSubject.next(currentUser) So canActivate is waiting for a new value but receives nothing.所以 canActivate 正在等待一个新值但什么也没收到。

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)一种方法是保持登录用户的状态,并基于该状态返回 boolean 或 url 树,甚至是可观察的(如果用户尚未加载并且您正在等待 http 响应)

  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.您也可以改用 BehaviorSubject 并决定它的最后一个值。

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通过这种方式,您可以防止您的数据被其他组件或服务意外更改

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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