5

在一个组件中,我使用商店为我的状态的某些部分订阅了一个主题。从那时起,当那部分状态发生变化时,我将开始接收更新,并且我的组件将反映此状态。到目前为止,一切都很好。

但是,我还希望我的组件能够根据此状态的当前值正确初始化。因此,一旦我订阅,我还希望发出当前值 - 例如,我的视图可以正确初始化。

带有 Angular 组件的示例:

我的.component.ts

export class MyComponent implements OnInit {
  constructor(private store: Store<State>) { }

  // As soon as I subscribe, I want this to also emit the current
  // value, e.g. so my view can correctly reflect the current state.
  public mode$ = new Subject<ApplicationMode>();

  ngOnInit() {
    this.store.select(getApplicationState)
       .select(s => s.applicationMode).subscribe(this.mode$);
  }
}

我的.component.html

{{ mode$ | async }}

我相信我想要/期望的是我商店的这一部分会返回 aBehaviorSubject或 a ReplaySubject(1)。但是从商店中选择任何东西总是返回一个Store<T>显然是某种主题的,但似乎在订阅时不会发出当前值?

我猜,可能的工作是在应用程序初始化时订阅这个状态,然后通过它的所有子组件传递这个值,一直到这个,所以这个组件变成了一个愚蠢的,而不是选择从商店本身。这可能是要走的路吗?还是我缺少一些基本的东西来完成这项工作?

4

1 回答 1

4

Ngrx 确实像一个 BehaviorSubject 一样,当您第一次订阅时,您可以在其中获取当前值。

问题可能是您在mode$订阅该主题之前将值从行为主题(存储)推送到普通主题()。这意味着您将初始值推送到您的主题 ( mode$) 中,并且它没有订阅者通知,因此它失败了。当订阅者订阅它时,mode$它正在订阅一个普通主题,因此它不会获得当前值,但会获得所有未来值。

我的建议是切断中间人并将从商店返回的可观察对象直接暴露给视图,如下所示:

this.mode$ = this.store.select(...);

这样你就暴露了一个 observable,它将在订阅任何订阅者时提供当前值。

我发现很难找到关于异步管道在组件生命周期中订阅位置的具体文档。因此,我进行了一个测试,在每个生命周期挂钩中发布到主题并通过异步管道订阅它。

编码:

import { 
  Component, 
  OnChanges, 
  OnInit, 
  DoCheck, 
  AfterContentInit, 
  AfterContentChecked, 
  AfterViewInit, 
  AfterViewChecked, 
  OnDestroy,
} from '@angular/core';

import { Subject } from 'rxjs/Subject';
import 'rxjs/add/operator/do';

@Component({
  selector: 'app-test',
  template: '{{ data$ | async }}'
})
export class TestComponent implements 
  OnChanges, 
  OnInit, 
  DoCheck, 
  AfterContentInit, 
  AfterContentChecked, 
  AfterViewInit, 
  AfterViewChecked, 
  OnDestroy {
    public get data$() {
      return this.data.asObservable().do(x => console.log('event: ', x));
    }
    public data: Subject<string> = new Subject<string>();


  ngOnChanges() {
    this.data.next('ngOnChanges');
  }

  ngOnInit() {
    this.data.next('ngOnInit');
  }

  ngDoCheck() {
    this.data.next('ngDoCheck');
  }

  ngAfterContentInit() {
    this.data.next('ngAfterContentInit');
  } 

  ngAfterContentChecked() {
    this.data.next('ngAfterContentChecked');
  }

  ngAfterViewInit() {
    this.data.next('ngAfterViewInit');
  }

  ngAfterViewChecked() {
    this.data.next('ngAfterViewChecked');
  }

  ngOnDestroy() {
    this.data.next('ngOnDestroy');
  }
}

结果:

event:  ngAfterViewInit
event:  ngAfterViewChecked
event:  ngDoCheck
event:  ngAfterContentChecked
event:  ngAfterViewChecked

结果表明异步管道首先在您第一次订阅的 ngOnInit 之后执行。这将解释你为什么错过它。

于 2017-11-15T15:29:32.830 回答