NgOnInit() 与构造函数并行运行 after/in

NgOnInit() Runs after/in parallel with constructor

  constructor(private firestore: AngularFirestore, private activatedRoute: ActivatedRoute) {
 this.firestore.collection('settings').doc('settings').valueChanges().subscribe(settings => {
  console.log('getting settings');
  this.settings = settings;
});
}
ngOnInit() {
this.activatedRoute.queryParams.subscribe(params => {
   console.log(this.settings);
  }
}

然而,我的控制台输出是

未定义

正在获取设置

我怎样才能让一个 运行 接一个?没有把所有的东西都放在 NgOnInit 中,整个代码都在 .then()

之后

编辑: 我已将代码编辑为下面建议的答案

使用 forkJoin:

  ngOnInit() {
forkJoin([this.firestore.collection('settings').doc('settings').valueChanges(),
  this.activatedRoute.queryParams]).subscribe(values => {
  this.settings = values[0];
  console.log('I am a console');
  console.log(this.settings);
  this.queryParams = values[1];
  if (this.queryParams['myParam']) {
    this.setmyParamTrue();
  }
});

但是,无论有无任何 queryParams,都不会进入控制台

不用说,我只需要从 firestore 中检索设置一次,而且参数不必存在。

有两个运算符可以帮助您相互使用 2 个可观察对象:

  1. forkJoin
  2. 合并最新
  3. switchMap

如果你需要运行,一旦你有两个,使用forkJoin如下:

constructor(
  private firestore: AngularFirestore, 
  private activatedRoute: ActivatedRoute) {
 
  forkJoin([
   this.firestore.collection('settings').doc('settings').valueChanges(),
   this.activatedRoute.queryParams
   ]).subscribe(values => {
      this.settings = values[0]; //<- you'll get settings here
      console.log(values[1]);
      // use the call accordingly.
   })
}

同样,如果需要连续收听,可以使用combineLatest。并使用 switchMap。完全取决于用例。

情况

来自您的原始代码:

1  class SomeComponent {
2  
3    constructor(private firestore: AngularFirestore, private route: ActivatedRoute) {
4      this.firestore.collection().doc().valueChanges().subscribe(settings => {
5        console.log('getting settings');
6        this.settings = settings;
7      });
8    }
9  
10   ngOnInit() {
11     this.route.queryParams.subscribe(params => {
12       console.log(this.settings);
13     }
14   }
15 }

在第 4 行的 constructor 中,您只是创建了一个 Subscription。您传递给 .subscribe() 方法的逻辑将在每次 observable 发出值时执行。

在第 11 行的 ngOnInit 中,您正在创建另一个订阅。

回调函数的执行顺序完全取决于每个 observable 发出的时间,而不是您注册订阅的时间! (显然,在您的情况下,ActivatedRoute 的可观察值首先发射,然后是 AngularFireStore 的观察值。

NgOnInit() Runs after/in parallel with constructor

希望你能明白为什么这个说法是不正确的!


目标

How can I make one run after the other?

您正尝试在回调中访问 this.settings,但尚未准备好!解决方案是创建一个仅在 settings 值准备就绪后才开始的可观察对象。

让我们将您的设置定义为可观察对象:

1  class SomeComponent {
2
3    private settings$ = this.firestore.collection().doc().valueChanges();
4    
5    constructor(private firestore: AngularFirestore, private route: ActivatedRoute) { }
6  
7    ngOnInit() {
8      this.settings$.pipe(
9        switchMap(settings => this.route.queryParams.pipe(
10         tap(params => console.log('both values', { settings, params }))
11       ))
12     ).subscribe();
13   }
14 }

您在第 8 行看到我们从 this.setting$pipe 开始到 switchMap 的排放,这将订阅内部源并发出源的排放。在这种情况下,您的内部源是可观察的参数。

我们将 pipe 放在可观察的内部参数上,这样 tap 就可以访问 settings 和发出的 params.

tap 对于执行副作用很有用,通常比将逻辑放在订阅回调中更受欢迎。


根据您在 ngOnInit 中订阅的目的,单独定义可观察对象可能会有所帮助,就像我们在 settings$:

中所做的那样
1  class SomeComponent {
2
3    private settings$ = this.firestore.collection().doc().valueChanges();
4    private doWork$ = this.settings$.pipe(
5      switchMap(settings => this.route.queryParams.pipe(
6        tap(params => console.log('both values', { settings, params }))
7      ))
8    );
9
10   constructor(private firestore: AngularFirestore, private route: ActivatedRoute) { }
11 
12   ngOnInit() {
13     this.doWork$.subscribe();
14   }
15 }