首页 > 解决方案 > 为什么组合的可观察对象在使用主题时不更新模板,或者它们在 ngAfterContentInit 之后发出

问题描述

我在一个组件中定义了一个 observable (result$),并通过异步管道将它显示在它的模板上。observable 是其他 2 个 observables (first$, second$) 通过 combineLatest 的组合。如果其中一个或两个 observable 发出得太早(在我找到 ngAfterContentInit 之前),则生成的 observable 不会发出值。

组件:不起作用

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {

result$: Observable<number>;
first$ = new Subject<number>();
second$ = new Subject<number>();

  constructor() {}

  ngOnInit(){
      this.result$ = combineLatest(
        this.first$,
        this.second$
      ).pipe(
        map(([first, second]) => {
          // This is not printed to the console
          console.log('combined obs emitted value');
          return first + second;
        })
      );
   console.log('first and second emit value');
   this.first$.next(2);
   this.second$.next(4);
  }

  ngAfterContentInit() {
      console.log('ngAfterContentInit');
  }
}

执行顺序为:

1.first 和 second 发射值

2.ngAfterContentInit

我在这里的假设是,在 ngAfterViewInit 中,模板已经被渲染并且订阅已经完成。因为可观察对象在此之前发出一个值,所以不会通知组件。这只能意味着生成的 observable 是冷的(因此,您需要在它发出值之前订阅)。2 个 observables 是 Subjects,所以我的假设是,subjects 是冷的 observables。这个对吗?

如果我延迟 first$ 和 second$ 的发射,一切正常: Component: first$ 和 second$ 稍后发射 @Component({ selector: 'my-app', templateUrl: './app.component.html', styleUrls: [ './app.component.css' ] }) 导出类 AppComponent {

result$: Observable<number>;
first$ = new Subject<number>();
second$ = new Subject<number>();

  constructor() {}

  ngOnInit(){

      this.result$ = combineLatest(
        this.first$,
        this.second$
      ).pipe(
        map(([first, second]) => {
          console.log('combined obs emitted value');
          return first + second;
        })
      );

    // Solution 1: add timeout
    setTimeout(() => {
      console.log('first and second emit value');
      this.first$.next(2);
      this.second$.next(4);
    })

  }

  ngAfterContentInit() {
      console.log('ngAfterContentInit');
  }
}

现在的顺序是:

  1. ngAfterContentInit

  2. 第一个和第二个发射值

  3. 组合 obs 发射值

再说一遍,这是因为订阅是在可观察对象发出值之前进行的吗?

如果我将 observables 更改为 BehaviorSubject,即使在订阅发生之前发出了值,一切也会正常工作。这是否意味着 BehaviourSubjects 是热门的可观察对象?组成部分:作品

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {

result$: Observable<number>;
first$ = new BehaviorSubject<number>(0);
second$ = new BehaviorSubject<number>(0);

  constructor() {

  }

  ngOnInit(){

this.result$ = combineLatest(
        this.first$,
        this.second$
      ).pipe(
        map(([first, second]) => {
          // This is not printed to the console
          console.log('combined obs emitted value');
          return first + second;
        })
      );


  console.log('first and second emit value');
   this.first$.next(2);
   this.second$.next(4);


  }

  ngAfterContentInit() {
      console.log('ngAfterContentInit');
  }
}

堆栈闪电战

标签: angularrxjsobservablebehaviorsubjectsubject

解决方案


Q1:这 2 个 observables 是 Subjects,所以我的假设是 subject 是冷 observables。这个对吗?

A1:这个问题的答案是主题本身很热门。这篇文章描述了热、冷和主题。

Q2:同样,这是因为订阅是在可观察对象发出值之前进行的吗?

A2:是的。订阅主题将在您订阅后收到值。您介绍的 delay() 应该有时间让这种情况发生。

为什么?这是因为 aBehaviorSubject始终保存一个值并在订阅时发出它,而 aSubject不保存一个值,它只是将值从生产者发送到当前订阅者。详情

Q3:这是否意味着 BehaviourSubjects 是热门的 observables?

答:见 A1。

如果这不能直接回答您的问题,我们深表歉意。我只是想说......在处理主题和行为主题时,我真的不在乎它们是热的还是冷的。

相反,我问:“我是否希望 observable 在我订阅时始终保持一个值?”。如果是,我使用BehaviorSubject. 如果我在订阅时没有任何价值就可以了,那就可以了Subject。除了这个差异之外,它们都将在订阅后收到发出的值。--- 取决于您的用例。


推荐阅读