在另一个服务(角度7)中检测服务中的更改

时间:2019-03-01 22:16:49

标签: angular dependency-injection rxjs observable angular-services

我现在只在Angular工作了几个月,目前对这些服务不知所措。我正在使用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);
      }));
  }

}

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

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;
      }));

错误消息:

  

类型“ BehaviorSubject”上不存在属性“ toLowerCase”。

我的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”类中访问。

1 个答案:

答案 0 :(得分: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,但只有在知道自己在做什么的情况下才能这样做,即,如果您确实想在特定时间获取当前值,请致电{ {1}}。请记住,您正在处理应用程序中的异步操作。如果要确保通过调用getValue设置了activeBusinessCase$的值,则必须订阅this.authService.setActiveBusinessCase。如果您不在乎是否已设置BehaviorSubject,而只想使用activeBusinessCase$的当前值,则可以使用activeBusinessCase$(然后可能会返回已设置的getValue作为null的初始值。

我的建议:

activeBusinessCase$