当变量在订阅中发生变化时,为什么视图没有更新?
我有这个代码:
example.component.ts
testVariable: string;
ngOnInit() {
this.testVariable = 'foo';
this.someService.someObservable.subscribe(
() => console.log('success'),
(error) => console.log('error', error),
() => {
this.testVariable += '-bar';
console.log('completed', this.testVariable);
// prints: foo-Hello-bar
}
);
this.testVariable += '-Hello';
}
example.component.html
{{testVariable}}
但视图显示:foo-Hello。
为什么不显示:foo-Hello-bar?
如果我在订阅中调用ChangeDetectorRef.detectChanges()
它将显示正确的值,但为什么我必须这样做?
我不应该从每个订阅中调用此方法,或者根本不应该(angular应该处理这个)。有正确的方法吗?
我是否遗漏了Angular / rxjs 5到6的更新内容?
现在我有Angular版本6.0.2和rxjs 6.0.0。相同的代码在Angular 5.2和rxjs 5.5.10中运行正常,无需调用detectChanges
。
据我所知,如果您更改“Angular区域”中的数据,Angular只会更新视图。您的示例中的异步调用不符合此条件。但是如果需要,可以将它放在Angular区域中或使用rxjs或将部分代码提取到新组件来解决此问题。我会解释所有:
1角区
此服务最常见的用途是在启动由一个或多个异步任务组成的工作时优化性能,这些异步任务不需要由Angular处理UI更新或错误处理。这些任务可以通过runOutsideAngular启动,如果需要,这些任务可以通过运行重新进入Angular区域。 https://angular.io/api/core/NgZone
关键部分是“运行”功能。您可以注入NgZone并将值更新放在NgZone对象的运行回调中:
constructor(private ngZone: NgZone ) { }
testVariable: string;
ngOnInit() {
this.testVariable = 'foo';
this.someService.someObservable.subscribe(
() => console.log('success'),
(error) => console.log('error', error),
() => {
this.ngZone.run( () => {
this.testVariable += '-bar';
});
}
);
}
根据this的回答,它会导致整个应用程序检测到更改,而您的ChangeDetectorRef.detectChanges方法只会检测组件及其后代的更改。
2 RxJS
另一种方法是使用rxjs
来更新视图。当您第一次订阅ReplaySubject时,它会为您提供最新价值。 BehaviorSubject基本相同,但允许您定义默认值(在您的示例中有意义,但不一定是正确的选择)。在这个初始发射后它基本上是一个正常的重播主题:
this.testVariable = 'foo';
testEmitter$ = new BehaviorSubject<string>(this.testVariable);
ngOnInit() {
this.someService.someObservable.subscribe(
() => console.log('success'),
(error) => console.log('error', error),
() => {
this.testVariable += '-bar';
this.testEmitter.next(this.testVariable);
}
);
}
在您的视图中,您可以使用async管道订阅主题:
{{testEmitter$ | async}}
3将代码提取到新组件
如果将字符串提交给另一个组件,它也将更新。您必须在新组件中使用@Input()
选择器。
所以新组件的代码如下:
@Input() testVariable = '';
并且使用大括号在HTML中分配测试变量。
在父HTML视图中,您可以将parentelement的变量传递给子元素:
<app-child [testVariable]="testVariable"></app-child>
这样你就处于Angular区域。
4个人偏好
我个人的偏好是使用rxjs或组件方式。使用detectChanges或者NGZone对我来说感觉更加hacky。