1

我对async-pipe 与ObservableOnInit. 必须是关于OnInit发生的时间点以及模板被渲染并因此Observable被订阅的时间点的时间问题。

考虑这个组件:

export class AppComponent implements OnInit {

    subjectA$: Subject<{name:string}>;
    subjectB$: Subject<{name:string}>;

    constructor(
        protected http: HttpClient
    ) {
    }

    ngOnInit() {
        this.subjectA$ = new Subject<{name: string}>();
        this.subjectA$.next({name: "A"});

        this.subjectB$ = new Subject<{name: string}>();
        setTimeout(() => {
          this.subjectB$.next({name: "B"});
        }, 0);
    }

}

和模板:

<p *ngIf='subjectA$ | async as subjectA; else: nosubjectA'>
  subjectA: {{subjectA.name}}
</p>

<ng-template #nosubjectA>
  <p>no subjectA</p>
</ng-template>

<p *ngIf='subjectB$ | async as subjectB; else: nosubjectB'>
  subjectB: {{subjectB.name}}
</p>

<ng-template #nosubjectB>
  <p>no subjectB</p>
</ng-template>

这导致

no subjectA

subjectB: B 

这意味着:即使在subjectA$中获得了值onInit,视图也不会更新。如果我在 a 中创建第一个值,setTimeout如您所见subjectB$,它可以工作并且我看到了值。虽然这是一个解决方案,但我想知道为什么会发生这种情况,是否有更好的解决方案?

我已经找到的一个解决方案是使用BehaviorSubject提供第一个值作为初始值:


        this.subjectC$ = new BehaviorSubject<{name: string}>({name: "C"});

与subjectC的类似模板导致subjectC: C

在 StackBlitz 上尝试一切。

我真正的 observable 根本不是Subject,而是combineLatest不同东西的 -call 的结果,其中只有一个是(不幸的是,因为它使用来自@Input()-annotation 的值) a Subject,并像示例next中一样手动推送OnInit. 其余的来自http等人。很可能我可以将组合结果包装在 a 中,BehaviourSubject但它对我来说看起来既丑陋又危险,所以它比这种setTimeout方法更糟糕。但我敢打赌,有人可以帮助我并找到一个真正有用的解决方案。另外,我宁愿避免BehaviorSubject使用getValue.

参见 Stackblitz

4

2 回答 2

2

一个快速的解决方法是使用ReplaySubject缓冲区 1 而不是BehaviorSubject. 您不必提供默认值,它既没有getValue()函数也没有valuegetter。然而,它会缓冲(或保存)最后一个发出的值,并在新订阅时立即发出它。

尝试以下

ngOnInit() {
  this.subjectA$ = new ReplaySubject<{name: string}>(1);
  this.subjectA$.next({name: "A"});

  this.subjectB$ = new ReplaySubject<{name: string}>(1);
  this.subjectB$.next({name: "B"});

  this.subjectC$ = combineLatest([this.subjectA$, this.subjectB$]).pipe(
    map((things: [{name:string}, {name:string}]): {name:string} => {
      return {name: things.map(thing => thing.name).join('|')}
    })
  );
}

我已经修改了您的Stackblitz

于 2020-07-24T13:54:51.307 回答
1

在我发表评论之后,我真的忍不住想一定有更好的方法——终于想到了一些可行的方法!

我刚刚修改了你的堆栈闪电战。

private valueA = "A";
private valueB = "B";

subjectA$ = of({ name: this.valueA });
subjectB$ = of({ name: this.valueB });
subjectC$ = combineLatest([this.subjectA$, this.subjectB$])
          .pipe(
            map((things: [{name:string}, {name:string}]): {name:string} => {return {name: things.map(x => x.name).join('|')}})
          );

这样,我们甚至可以丢弃ngOnInit钩子,一切正常!

于 2020-07-27T14:49:39.420 回答