startWith返回旧值

时间:2019-08-22 09:57:53

标签: angular rxjs

我为此苦了很长时间。我简化了我公司中的应用程序:

正在工作的堆叠example

app.component.ts

import { Component } from '@angular/core';
import { Subject } from 'rxjs';
import {map, startWith, takeUntil, tap} from "rxjs/operators";

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  public visible = false;
  private dateChangedSubj = new Subject();
  public dateChanged$ = this.dateChangedSubj.asObservable();

  private state = {
    date: "11.02.2019"
  };

  public date$;

  constructor() {
    this.date$ = this.dateChanged$.pipe(
      startWith(this.state.date),
    )
  }

  show() {
    this.visible = true;
  }

  hide() {
    this.visible = false;
  }

  changeDate() {
    this.state.date = "12.02.2019";
    this.dateChangedSubj.next(this.state.date);
  }

}

app.component.html

<button (click)="show()">Show</button>
<button (click)="hide()">Hide</button>
<button (click)="changeDate()">Change date</button>

<div *ngIf="visible">
  <p>This one I can hide: {{ date$ | async }}</p>
</div>

<p>This is always visible: {{date$ | async}}</p>

我将配置存储在一个对象中。在这种情况下,它是state对象。我在那里存储一个简单的字符串值。我可以通过单击changeDate按钮来更改此值。然后它将更改对象的值,并通过Subject发送该新值,因此view可以被更新。但是,如果在更改发生后显示元素,则该值保持不变。

复制步骤:

  • 点击changeDate按钮
  • 显示的值正确
  • 点击Show按钮
  • 即使state已更新,新显示的值还是旧值

2 个答案:

答案 0 :(得分:1)

this article中所述,不建议为同一可观察对象创建多个异步管道。 这是因为每次使用异步管道时,也会创建一个新的订阅。

在下面的代码段中,

<div *ngIf="visible">
  <p>This one I can hide: {{ date$ | async }}</p>
</div>

visible = true时将创建一个新的订阅。
并且因为您的代码中包含了

this.date$ = this.dateChanged$.pipe(
 startWith(this.state.date),
 tap(v => console.log('emitted state value', v)), // The previous value
)

它将发出this.state.date的先前值。

当您再次显示日期时,您还可以看到每次创建新订阅。

这是一个解决方案,可以确保在给定的可观察对象上仅使用一次异步管道。

<ng-container *ngIf="(date$ | async) as date">
  <div *ngIf="visible">
    <p>This one I can hide: {{ date }}</p>
  </div>

  <p>This is always visible: {{ date }}</p>

</ng-container>

使用这种方法,只会创建一个订阅。

Here is a StackBlitz example。 另外,请确保检查控制台,以便更好地了解正在发生的事情。(这就​​是我实际上解决问题的方式)

答案 1 :(得分:0)

您可以使用BehaviorSubject代替Subject,并且不需要startWith。这样,您将拥有最后发出的值,并且它将始终是真实的。

链接到更新的Stackblitz:https://stackblitz.com/edit/angular-h6dk7h