【问题标题】:Angular - display updated behaviorsubject value in template when value changesAngular - 当值更改时在模板中显示更新的 behaviorsubject 值
【发布时间】:2022-11-23 23:54:20
【问题描述】:

我有一个布尔值 BehaviorSubject,我想在我的模板中使用它的值:

isLoggedIn$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);

在 ngOnInit 中,我订阅了它:

this.isLoggedIn$.subscribe((result) => {
  console.log('result', result);
});

之后(在 ngOnInit 中),我有一个返回承诺的服务调用。完成后,我设置行为主题值:

  this.authService.isLoggedIn().then((loggedIn) => {
    if (loggedIn) {
      this.isLoggedIn$.next(loggedIn);
      console.log('loggedIn', loggedIn);
      // set the user model value here...
    }
  });

最后,我在模板中显示了这个值:

<span>{{ isLoggedIn$ | async }}</span>

但是,即使控制台显示值发生变化,模板也没有更新。我尝试了 ngAfterViewInit 和更改检测,但似乎没有任何效果。我假设值没有在模板中更新的原因是因为当行为主题更新时,angular 认为没有任何改变。

我错过了什么吗?

问题是可能需要一两秒钟才能从 authService.isLoggedIn 承诺中取回值,如果延迟时间更长,模板将不会显示承诺中的数据(登录信息,如用户名)。唯一可行的是,如果我在服务调用周围使用 setTimeout() 包装器,我不想这样做。

有什么建议么?

编辑:这是整个组件,按照 Brandon 的建议添加 Observable。仍然不起作用:

import { Component, OnInit } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { IUserModel } from './_interfaces/user';
import { AppService } from './_services/app.service';
import { AuthService } from './_services/auth.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
})

export class AppComponent implements OnInit {
  apiUser?: IUserModel;
  isLoggedIn$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
  isLoggedInOb: Observable<boolean> = new Observable<boolean>;

  constructor(
    private readonly authService: AuthService,
    private readonly appService: AppService) {
  }

  ngOnInit(): void {
    this.isLoggedInOb.subscribe((result) => {
      this.isLoggedIn = result;
      console.log('result', result);
    });

    setTimeout(() => {
      this.authService.isLoggedIn().then((loggedIn) => {
        this.isLoggedIn$.next(loggedIn);
        this.isLoggedInOb = this.isLoggedIn$.asObservable();
        console.log('loggedIn', loggedIn);

        this.appService.lastActivityDatetimeSubject$.subscribe((data) => {
          this.lastActivityDatetime = data;
        });
      });
    }, 0);

    this.appService.apiUserSubject$.subscribe((data) => {
      this.apiUser = data;
    });

  }

  login(): void {
    this.authService.login();
  }

  logout(): void {
    this.authService.logout();
  }
}

如果我将 setTimeout 设置为 1000 毫秒,它会起作用,但如果我删除 setTimeout 函数则不起作用。

这是模板:

<header>
  <div class="header-group-1">
    <a href="index.html" class="header-logo-link">
      <img src="assets/images/logo.png" class="header-logo-img" alt="Company Logo" />
    </a>
    <div class="header-logo-text-wrapper">
      <span class="header-logo-text-top">Company Name</span>
    </div>
  </div>
  <div>
    <div class="header-group-2">
      <div class="header-app-name">
        <span>App Name</span>
      </div>
      <div class="header-greeting">
        <span *ngIf="apiUser && isLoggedIn && apiUser.FirstName">Hello {{apiUser.FirstName}} {{apiUser.LastName}}!</span>
        <button class="focus-dark" *ngIf="!isLoggedIn" mat-stroked-button (click)="login()">Login</button>
        <button class="focus-dark" *ngIf="isLoggedIn" mat-stroked-button (click)="logout()">Logout</button>
        
        {{ isLoggedInOb | async }} <--- this is what I am trying to have updated when the behavior subject is updated
        
      </div>
    </div>
  </div>
</header>

<div class="router-outlet-content" role="main">
  <router-outlet></router-outlet>
</div>
<footer class="footer" role="contentinfo">
  <div class="footer-first-row">
    <a href="/disclaimer">Disclaimer</a>
    <span class="divider" aria-hidden="true">|</span>
    <a href="/privacy-policy">Privacy Policy</a>
    <span class="divider" aria-hidden="true">|</span>
    <a href="/terms-conditions">Terms and Conditions</a>
  </div>
</footer>

【问题讨论】:

  • 在控制台中,您看到“结果为假,结果为真,登录为真”,对吧?
  • 对,那是正确的。控制台正在记录新值

标签: angular rxjs behaviorsubject


【解决方案1】:

我通常为 Subject/BehaviorSubject 提供一个可观察对象:

isLoggedIn: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
isLoggedIn$: Observable<boolean>;

constructor() {
  this.loggedIn$ = this.isLoggedIn.asObservable();
}

然后组件订阅可观察对象,而不是直接订阅 Subject/BehaviorSubject。

【讨论】:

  • 感谢布兰登的建议。我仍然有同样的问题。如果我遗漏了一些东西,我会发布更多代码
  • 您需要在组件的构造函数中从行为主题填充可观察对象。您使用 isLoggedIn() 的承诺有什么特别的原因吗?
  • 另外,我假设您将需要检查用户是否在您应用程序的其他地方登录?如果是这样,您将希望将该 BehaviorSubject/Observable 移动到服务而不是在组件中定义它们
  • 好的,我会尝试将其放入构造函数中。使用承诺的服务来自我们的 IDP 服务器(身份服务器),所以我认为我无法更改它。它正在使用 OidcClient 库
  • 是的,我可能会把它变成一项服务,但我只是想让它保持简单以证明概念。
【解决方案2】:

在服务中:

  private isLoggedIn: BehaviorSubject<boolean> = new 
    BehaviorSubject<boolean>(false);
  isLoggedIn$ = this.isLoggedIn.asObservable();

isLoggedIn(){
return this.http.post(url).pipe(
map(data)=>{
//...code
this.isLoggedIn.next(data)
//...code
}
)
}

在组件中

currentUser$:Observable<UserInteface>
  ngOnInit(): void {
this.currentUser$= 
    this.service. isLoggedIn$
}

在 HTML 中 您可以订阅它并使用异步管道取消订阅

<div *ngIf="(currentUser$ | async) as user">
{{user.nameExemple}}
//some code 
</div>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-01-20
    • 2021-06-05
    • 2018-03-22
    • 2021-11-06
    • 2019-05-12
    • 1970-01-01
    • 2013-12-05
    • 2017-11-01
    相关资源
    最近更新 更多