检测另一个服务中的服务内的更改(Angular 7)

问题描述 投票:1回答:1

我现在只用角度工作了几个月,现在已经不知所措了。我正在使用3个互相交互的角度服务。 authService,configService和guardService。

在我的guardService中,checkBusinessCase(bcase)方法检查当前的业务案例,并使用我的AuthService中的方法this.authService.setActiveBusinessCase(bcase)进行设置。

guardService看起来像这样:

import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router';
import { Observable, of } from 'rxjs';
import { UserAuthService } from './user-auth.service';
import { Constants } from '../../constants';
import { catchError, map } from 'rxjs/operators';

@Injectable()
export class BcaseRequiredGuard implements CanActivate {

  constructor(private router: Router,
              private authService: UserAuthService) {

  }

  canActivate(next: ActivatedRouteSnapshot,
              state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
    if (next.data['bcase']) {
      return this.checkBusinessCase(next.data['bcase']);
    }
    if (next.data['bcaseRegExp']) {
      return this.checkBusinessCase(new RegExp(next.data['bcaseRegExp']));
    }
    this.router.navigate([Constants.routing.error, 'invalidBusinessCase']);
    return false;

  }

  checkBusinessCase(bcase): Observable<boolean> {
    return this.authService.isLoggedIn().pipe(
      map(isLoggedIn => {
        const accept = isLoggedIn && this.authService.hasBusinessCase(bcase);
        if (accept) {
          console.log('This guard approved that the Business case ' + bcase + ' is assigned to ' + this.authService.user.user_name);
          this.authService.setActiveBusinessCase(bcase);
        } else {
          console.log('This guard approved that the Business case ' + bcase + ' is not assigned to ' + this.authService.user.user_name);
          this.router.navigate([Constants.routing.home]);
        }
        return accept;
      }),
      catchError((err) => {
        console.error(Constants.errors.loginCheckFailed, err);
        this.router.navigate([Constants.routing.error, 'loginCheckFailed']);
        return of(false);
      }));
  }

}

正是这个商业案例我现在需要在getAutoConfiguredBCLanguage()方法中的configService中。当我尝试使用this.authService.activeBusinessCase访问它时,我得到了未定义,因为在authService之前调用了configService。

configService如下所示:

import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import 'moment/locale/de';
import { UserAuthService } from 'app/auth/user-auth.service';

@Injectable()
export class ConfigurationService {

  defaultLanguage = 'en';
  activeBusinessCase = '';

  languages = {
    en: 'English',
    de: 'German',
    es: 'Spanish',
    ja: 'Japanese',
    pt: 'Portuguese'
  };

  // Business cases with available translation files
  businessCases = {
    case1: '_case1',
    case2: '_case2',
    case3: '_case3'
  };

  constructor(
    private translate: TranslateService,
    private authService: UserAuthService
  ) {

  }
  // Get business case specific language
  // TODO: Get currently logged in business case
  getAutoConfiguredBCLanguage(){
    const browserLang = this.translate.getBrowserLang();
    console.log('BROWSER LANGUAGE: ', browserLang);
    console.log('ACTIVE BUSINESS CASE: ', this.activeBusinessCase);
    if (this.languages.hasOwnProperty(browserLang) && this.activeBusinessCase !== undefined) {
      switch (this.activeBusinessCase) {
        case 'CASE1':
          return browserLang.concat(this.businessCases.case1);
        case 'CASE2':
          return browserLang.concat(this.businessCases.case2);
        case 'CASE3':
          return browserLang.concat(this.businessCases.case2);
        default:
          return browserLang;
      }
    } else if (this.languages.hasOwnProperty(browserLang) && this.activeBusinessCase === undefined) {
      console.log('No currently active business case.');
      return browserLang;
    } else {
      return this.defaultLanguage;
    }
  }

}

这是authService,其中设置了我需要的值的activeBusinessCase属性:

import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { Constants } from '../../constants';
import { EMPTY, Observable } from 'rxjs';
import { PathLocationStrategy } from '@angular/common';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { translate } from '../shared/translation-util';
import { environment } from '../../environments/environment';
import { LoginError } from '../model/auth/LoginError';
import { User } from '../model/auth/User';
import { UiInfo } from '../model/auth/UiInfo';
import { ChangePasswordError } from '../model/auth/ChangePasswordError';
import { Title } from '@angular/platform-browser';
import { catchError, map, mergeMap, publishReplay, refCount, tap } from 'rxjs/operators';
import { LoginResponse } from '../model/auth/LoginResponse';

@Injectable()
export class UserAuthService {

  activeBusinessCase: string;

  constructor(private router: Router,
              private httpClient: HttpClient,
              private pls: PathLocationStrategy,
              private titleService: Title) {

  }

  setActiveBusinessCase(bcase: string) {
    this.activeBusinessCase = bcase;
    this.setTitle(bcase);
  }

}

如何在configService中获取当前的业务案例(activeBusinessCase)?


更新:

在采用fridoo的变化并根据我的需求调整它们之后,现在一切看起来更合乎逻辑。但现在我在其他地方有错误消息,我无法处理。以下是控制台中的代码和错误消息。

在我的http.service.ts中:

  getResource(key: string, lang: string): Observable<any> {
    const headers = new HttpHeaders({'Accept': 'text/html'});
    return this.httpClient.get('/resources/' + key,
      {
        headers: headers,
        responseType: 'text',
        params: new HttpParams()
          .set('businessCase', this.authService.activeBusinessCase ? this.authService.activeBusinessCase :
            environment.default_business_case)
          .set('lang', lang)
      }).pipe(
      catchError(() => {
        return this.translateService.get('Not-available').pipe(
          map(res => '<h4 style="text-align: center">' + res + '</h4>'));
      })
    );
  }

错误信息:

属性“activeBusinessCase”是私有的,只能在“UserAuthService”类中访问。

我的user-auth.service.ts

  login(name: string, password: string, imTid: string): Observable<UiInfo> {
    return this.loginWithBackend(name, password, imTid).pipe(
      tap(() => {
        this.user.user_name = translate('default-user');
        // TODO: Check if loggedoff is obsolete?
        if (this.loggedOff) {
          this.pls.back();
        } else if (this.redirectUrl) {
          this.router.navigate([this.redirectUrl]);
          this.redirectUrl = null;
          console.log(Constants.texts.loginSuccessRedirect);

        } else {
          console.log('Active Business Case', this.activeBusinessCase);
          if (this.activeBusinessCase) {
            this.router.navigate([Constants.routing.explorer + this.activeBusinessCase.toLowerCase()]);
          } else {
            const err = new LoginError('Business case is missing');
            throw err;
          }
        }
        this.loggedOff = false;
      }));

错误信息:

属性'toLowerCase'在'BehaviorSubject'类型上不存在。

我的home.component.ts

export class HomeComponent implements OnInit {

  routing = Constants.routing;

  constructor(private router: Router,
              private authService: UserAuthService) {

  }

  ngOnInit() {
    this.router.navigate([Constants.routing.explorer + this.authService.activeBusinessCase.toLowerCase()]);
  }

}

错误信息:

属性“activeBusinessCase”是私有的,只能在“UserAuthService”类中访问。

angular dependency-injection rxjs observable angular-services
1个回答
1
投票

由于activeBusinessCase是异步设置的,并且您希望在应用程序的另一部分中收到有关值更改的通知,因此它应该是Subject。使用BehaviorSubject,以便您在订阅时立即获得当前值。

// ----- UserAuthService -----

public activeBusinessCase$ = new BehaviorSubject<string>(null);

setActiveBusinessCase(bcase: string) {
  this.activeBusinessCase.next(bcase);
  this.setTitle(bcase);
}

在ConfigurationService中监听activeBusinessCase$的值更改,并将传入值映射到要返回的语言。

// ----- ConfigurationService -----

// will emit a new language every time activeBusinessCase changes,
// this behavior could be changed depending on your specific needs
getAutoConfiguredBCLanguage$(): Observable<string> {
  return this.authService.activeBusinessCase$
    .pipe(
      // use 'distinctUntilChanged' to only emit values that are different from the previous,
      distinctUntilChanged(),
      map(abc => this.getAutoConfiguredBCLanguage(abc))
    );     
}

private getAutoConfiguredBCLanguage(activeBusinessCase: string): string {
  // no changes here apart from the input parameter - I omitted the code for brevity
  // use the parameter activeBusinessCase instead of this.activeBusinessCase here
}

订阅getAutoConfiguredBCLanguage$您需要的语言。

// ----- AppComponent -----

initLanguage() {
  this.configService.getAutoConfiguredBCLanguage$()
    .pipe(takeUntil(this.destroy$)) // unsubscribe when the component gets destroyed
    .subscribe(lang => {
      // use the language here
    });
}

private destroy$ = new Subject<void>();

onDestroy() {
  this.destroy$.next();
  this.destroy$.complete();
}

BehaviorSubject具有函数getValue,它返回BehaviorSubject发出的最后一个值。您可以使用此函数来获取当前的activeBusinessCase,但只有在您知道自己在做什么时才这样做,即如果您真的想要在调用getValue的特定时间获取当前值。请记住,您正在处理应用程序中的异步操作。如果你想通过调用activeBusinessCase$来确保获得this.authService.setActiveBusinessCase的值,你必须订阅BehaviorSubject。如果你不关心activeBusinessCase$是否已经设置并且只想使用activeBusinessCase$的当前值,你可以使用getValue(然后可能返回null,它被设置为activeBusinessCase$的初始值)。

我的建议:

// ---- HomeComponent ----

ngOnInit() {
  // To navigate to a route once the activeBusinessCase has a non null value
  // subscribe but only take the first non null element and no further values afterwards
  this.authService.activeBusinessCase$
    .pipe(first(Boolean))
    .subscribe(bc => this.router.navigate([Constants.routing.explorer + bc.toLowerCase()]));
}

// ---- SmagHttpService & UserAuthService ----

// Don't subscribe to activeBusinessCase$ in here!! Instead use
this.authService.activeBusinessCase$.getValue()
// when you need the current value (which might not have been set yet, 
// but you already have null checks here and if this part of your app worked before 
// there will be no differences)

// Example:
const businessCase = this.authService.activeBusinessCase$.getValue();
if (businessCase) {
  this.router.navigate([Constants.routing.explorer + businessCase.toLowerCase()]);
}

businessCase ? businessCase : environment.default_business_case
© www.soinside.com 2019 - 2024. All rights reserved.