0

我正在构建一个类似于此的角度架构: https ://blog.angular-university.io/how-to-build-angular2-apps-using-rxjs-observable-data-services-pitfalls-to-avoid/

我只有一个用于管理状态的基类,它管理特定模块的状态:

import { BehaviorSubject, Observable } from 'rxjs';
import { distinctUntilChanged, map, take } from 'rxjs/operators';

export class StateService<T> {
  private state$: BehaviorSubject<T>;
  protected get state(): T {
    return this.state$.value;
  }

  constructor(initialState: T) {
    this.state$ = new BehaviorSubject<T>(initialState);
  }

  protected select<K>(mapFn: (state: T) => K): Observable<K> {
    return this.state$.asObservable().pipe(
      map((state: T) => mapFn(state)),

    );
  }

  protected clearState(){
    this.state$.next(null as any);
  }

  protected setState(newState: Partial<T>) {
    this.state$.next({
      ...this.state,
      ...newState,
    });
  }
}

所以为了管理状态,你只需要简单地扩展这个类

import { HttpClient, HttpErrorResponse, HttpHeaders } from "@angular/common/http";
import { Injectable } from "@angular/core";
import { Observable, ObservableInput, throwError } from "rxjs";
import { catchError, shareReplay, tap } from 'rxjs/operators';
import { StateService } from "src/app/core/services/state-service.service";
import { AuthHttpClient } from "./auth-httpclient.service";
import { UserInfo } from "./state-models/user-info.model"



@Injectable({
  providedIn: 'root'
})
export class AuthService extends StateService<UserInfo>{

    protected _tokenExpirationTimer:any;
    protected tokenExpirationDuration = 3600;
    public userInfoattempt$ : Observable<UserInfo> = this.select(state => state);

    public oauthTriggerState$ : Observable<boolean> = this.select(state => state.oauthTriggerSignup);
    constructor(){
      super(null as any);
    }

    public autoLogout(expirationDuration:number){
      this._tokenExpirationTimer = setTimeout(() => {
        this.logoutUser();
      }, expirationDuration * 1000);
    }

    public logoutUser(){
      this.clearState();
      localStorage.clear();
      if(this._tokenExpirationTimer)
        clearTimeout(this._tokenExpirationTimer);
      this._tokenExpirationTimer = null;
    }

    protected handleError(errorResp:HttpErrorResponse):ObservableInput<any>{
      if(errorResp.error.errors) {
        console.log("Error:",errorResp);
        return throwError(errorResp.error.errors);
      }
      return undefined as unknown as ObservableInput<any>;
    }

  public loginUser(userCredentials:UserCredentials,provider?:string) {
    return this.api.request<UserSignedinData,UserSignedinDataAdd>( provider ? `api/auth/signin?provider=${provider}` : 'api/auth/signin','post',null as any,userCredentials)
    .pipe(
      tap(val => {
        if(val.isSuccessfull){
          var decodedToken : Partial<UserInfo> = jwt_decode(val.data.Token);
          var user = new UserInfo(
            val.data.Token,
            decodedToken.id,
            decodedToken.email,
            decodedToken.exp,
            decodedToken.username,
            decodedToken.phone
            );
          this.setState(user);
          // add to local storage
          localStorage.setItem("user",JSON.stringify(user));
          //session expiry
          this.autoLogout(this.tokenExpirationDuration);
        }
        else{
          throw new Error("Some Error Occured!");
        }
        //temporarily throw error
      }),
      catchError(this.handleError)
    )
  }

}



这里感兴趣的方法是 loginUser,正如您在 tap 运算符中看到的那样,它基本上调用 setState 方法,该方法在父级中的 behviourSubject 上调用 .next(),并且显然新值反映给 userInfoattempt$ 可观察的所有订阅者,所以当我在一个组件中注入这个服务并订阅 userInfoattempt$ 时,只要有变化,我就会得到新的值,这工作正常,直到我让另一个服务说 signinService 扩展这个 authService 并且我把登录方法放在那里,

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { catchError, shareReplay, tap } from 'rxjs/operators';
import { AuthHttpClient } from '../auth-httpclient.service';
import { AuthService } from '../auth.service';
import { UserCredentials } from '../state-models/user-credentials.model';
import { UserInfo } from '../state-models/user-info.model';
import { UserSignedinDataAdd } from '../state-models/user-signedin-data-add.model';
import { UserSignedinData } from '../state-models/user-signedin-data.model';
import jwt_decode from 'jwt-decode';
import { UserSignedupData } from '../state-models/user-signedup-data.model';
import { UserSignupCredentials } from '../state-models/user-signup-credentials.model';
import { UserSingedupDataAdd } from '../state-models/user-singedupdataadd.model';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class SigninService extends AuthService {
  public userInfo$ : Observable<UserInfo> = this.select(state => state);

  constructor(private api:AuthHttpClient,private http:HttpClient) {
    super();
   }
  public loginUser(userCredentials:UserCredentials,provider?:string) {
    return this.api.request<UserSignedinData,UserSignedinDataAdd>( provider ? `api/auth/signin?provider=${provider}` : 'api/auth/signin','post',null as any,userCredentials)
    .pipe(
      tap(val => {
        if(val.isSuccessfull){
          var decodedToken : Partial<UserInfo> = jwt_decode(val.data.Token);
          var user = new UserInfo(
            val.data.Token,
            decodedToken.id,
            decodedToken.email,
            decodedToken.exp,
            decodedToken.username,
            decodedToken.phone
            );
          this.setState(user);
          // add to local storage
          localStorage.setItem("user",JSON.stringify(user));
          //session expiry
          this.autoLogout(this.tokenExpirationDuration);
        }
        else{
          throw new Error("Some Error Occured!");
        }
        //temporarily throw error
      }),
      catchError(this.handleError)
    )
  }
  public autoLogin(){
    const user = JSON.parse(localStorage.getItem('user')!) as UserInfo;
    if(!user)
      return;
    this.setState(user);
    var remainingTime = this.tokenExpirationDuration;
    this.autoLogout(remainingTime);
  }
}


这就是问题发生的地方,这里的 loginUser 方法是相同的,但是当我向组件“注入 authService(不是 signinService)”并订阅 userInfoattempt$ 时,如果我将 signinService 注入组件和订阅 userInfo$ 会反映新值,尽管它们都指向同一个 BehaviourSubject,但我在这里遗漏了什么吗?

4

0 回答 0