当我订阅可观察对象之前,异步管道无法正常工作时,我遇到了一个奇怪的情况。来源:
该组件的视图:
<div *ngIf="loading$ | async" fxLayout="row" fxLayoutAlign="center">
<span class="spinner">Loading...</span>
</div>
<h1>{{data$ | async}}</h1>
组件:
@Component({
selector: './tm-fake',
changeDetection: ChangeDetectionStrategy.OnPush,
templateUrl: './fake.component.html'
})
export class FakeComponent implements OnInit {
constructor(
private _fs: FakeService
) {}
loading$ = new BehaviorSubject<boolean>(false);
data$ = this._fs.data$.pipe(indicateUntilNULL(this.loading$));
ngOnInit(): void {
this._fs.requestData();
//this.data$.subscribe(e => {});
}
}
服务:
@Injectable()
export class FakeService {
requestData(): void {
timer(2000).pipe(map(e => "data")).subscribe(e => this._data.next(e));
}
private readonly _data = new BehaviorSubject<string>(null);
readonly data$ = this._data.asObservable();
}
操作员:
export function indicateUntilNULL<T>(indicator: BehaviorSubject<boolean>): (source: Observable<T>) => Observable<T> {
return source => source.pipe(
tap(e => indicator.next(!e))
);
}
我期望延迟2秒后看到data
,但是在此延迟期间我看不到旋转器。但是,如果我取消注释行this.data$.subscribe(e => {});
,它将开始正常工作。而且我找不到原因。有什么想法吗?
NB:loading$
发出正确的值,即使此行被注释。
解决方案
非常感谢@theMayer及其有用的建议。我只想添加article,其中详细说明了问题。根据本文,最优雅的解决方案是delay(0)
运算符:
export function indicateUntilNULL<T>(indicator: BehaviorSubject<boolean>): (source: Observable<T>) => Observable<T> {
return source => source.pipe(
delay(0),
tap(e => indicator.next(!e))
);
}
答案 0 :(得分:0)
好的,在检查完您的代码后,我认为我理解了这个问题。您不会在DOM中看到通过true
观察到的loading$
发出的更新值。
这是您的代码将要发生的事情:
将构造对象,并将行为对象_data
初始化为空值。
Angular模板将在其中渲染并进行更改检测。它将为false
的值拾取loading
,为null
的值data
。载入中的微调器将不会旋转。
在更改检测周期之后,async
管道将触发true
发出的loading
值。这是同步发生的。由于更改检测设置为OnPush
,因此我怀疑此更改会被遗漏,因为设置该值时已经存在一个更改检测周期(通常,常规更改检测策略可能会捕获此更改,并且引发错误)。
然后您的data
在两秒钟的延迟后发出。
请注意,使用常规更改检测时,这种情况将导致ExpressionChangedAfterItHasBeenCheckedError
。正确的方法是在更改检测周期之前确保组件的内部一致性,而不是在更改检测期间执行触发进一步更改的操作。
快速的答案是将“ loading”值初始设置为true,因为代码在初始化时将始终尝试加载数据。
答案 1 :(得分:-1)
您需要share()
,这是我的工作示例:
public $route!: Observable<{}>;
constructor(
private route: ActivatedRoute
) { }
public ngOnInit(): void {
this.$route = this.route.data.pipe(share());
}
然后在视图中
<ng-container *ngIf="$route | async as route; else loading">
...
</ng-container>