Ngrx:订阅时未在主题中收到当前值
Ngrx: current value not received in Subject upon subscription
在一个组件中,我使用商店订阅了我状态的某些部分的主题。从那时起,当那部分状态发生变化时,我将开始接收更新,我的组件将反映该状态。到目前为止一切顺利。
但是,我还希望我的组件根据此状态的 current 值正确地 initialize。因此,一旦我订阅,我还希望发出 current 值 - 因此,例如,我的视图可以正确初始化。
带有 Angular 组件的示例:
my.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$);
}
}
my.component.html
{{ mode$ | async }}
我相信我 like/expect 是我商店的这一部分 return BehaviorSubject
或 ReplaySubject(1)
。但是从商店中选择任何东西总是 returns a Store<T>
这显然是某种主题,但似乎不会在订阅时发出当前值?
我猜,可行的方法是在应用程序初始化时订阅这个状态,然后将该值传递给它的所有子组件,一直传递到这个组件,这样这个组件就变成了愚蠢的而不是从商店本身选择。这也许是要走的路吗?还是我缺少一些基本的东西来完成这项工作?
Ngrx 确实像一个 BehaviorSubject,您在第一次订阅时会获得当前值。
问题可能是您在订阅该主题之前将行为主题(存储)的值推送到普通主题(mode$
)。这意味着您将初始值推入您的主题 (mode$
),并且它没有订阅者可以通知,所以它失败了。当订阅者订阅 mode$
时,它订阅的是普通主题,因此它不会获得当前值,但会获得所有未来值。
我的建议是去掉中间人,并将 store 返回的 observable 直接暴露给视图,如下所示:
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 之后执行。这可以解释您错过它的原因。
在一个组件中,我使用商店订阅了我状态的某些部分的主题。从那时起,当那部分状态发生变化时,我将开始接收更新,我的组件将反映该状态。到目前为止一切顺利。
但是,我还希望我的组件根据此状态的 current 值正确地 initialize。因此,一旦我订阅,我还希望发出 current 值 - 因此,例如,我的视图可以正确初始化。
带有 Angular 组件的示例:
my.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$);
}
}
my.component.html
{{ mode$ | async }}
我相信我 like/expect 是我商店的这一部分 return BehaviorSubject
或 ReplaySubject(1)
。但是从商店中选择任何东西总是 returns a Store<T>
这显然是某种主题,但似乎不会在订阅时发出当前值?
我猜,可行的方法是在应用程序初始化时订阅这个状态,然后将该值传递给它的所有子组件,一直传递到这个组件,这样这个组件就变成了愚蠢的而不是从商店本身选择。这也许是要走的路吗?还是我缺少一些基本的东西来完成这项工作?
Ngrx 确实像一个 BehaviorSubject,您在第一次订阅时会获得当前值。
问题可能是您在订阅该主题之前将行为主题(存储)的值推送到普通主题(mode$
)。这意味着您将初始值推入您的主题 (mode$
),并且它没有订阅者可以通知,所以它失败了。当订阅者订阅 mode$
时,它订阅的是普通主题,因此它不会获得当前值,但会获得所有未来值。
我的建议是去掉中间人,并将 store 返回的 observable 直接暴露给视图,如下所示:
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 之后执行。这可以解释您错过它的原因。