在订阅中更改变量后,Angular 6 View不会更新 [英] Angular 6 View is not updated after changing a variable within subscribe

查看:302
本文介绍了在订阅中更改变量后,Angular 6 View不会更新的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当订阅中的变量发生更改时,为什么视图不更新?

Why is the view not being updated when a variable changes within a subscribe?

我有此代码:

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(),它将显示正确的值,但是为什么我必须这样做?

If I call ChangeDetectorRef.detectChanges() within the subscribe it will display the proper value, but why do I have to do this?

我不应该从每个订阅中调用此方法,或者根本不应该(角度应处理此方法).有正确的方法吗?

I shouldn't be calling this method from every subscribe, or, at all (angular should handle this). Is there a right way?

我从Angular/rxjs 5更新到6时错过了什么吗?

Did I miss something in the update from Angular/rxjs 5 to 6?

现在我有Angular版本6.0.2和rxjs 6.0.0.相同的代码在Angular 5.2和rxjs 5.5.10中可以正常工作,而无需调用detectChanges.

Right now I have Angular version 6.0.2 and rxjs 6.0.0. The same code works ok in Angular 5.2 and rxjs 5.5.10 without the need of calling detectChanges.

推荐答案

据我所知,如果您在"Angular zone"中更改数据,Angular只会更新视图.您的示例中的异步调用不符合此要求.但是,如果需要,可以将其放在Angular区域中,或使用rxjs或将部分代码提取到新组件中以解决此问题.我将解释所有内容:

As far as I know, Angular is only updating the view, if you change data in the "Angular zone". The asynchronous call in your example does not qualify for this. But if you want, you can put it in the Angular zone or use rxjs or extract part of the code to a new component to solve this problem. I will explain all:

似乎并非所有解决方案都在起作用.对于大多数用户而言,第一个解决方案"Angular Zone"可以完成这项工作.

It seems like not all solutions are working anymore. For most users the first Solution "Angular Zone" does the job.

1个角度区域

此服务最常见的用途是在启动一项工作时优化性能,该工作由一个或多个不需要Angular处理UI更新或错误处理的异步任务组成.可以通过runOutsideAngular启动此类任务,如果需要,可以通过 run 重新进入Angular区域. https://angular.io/api/core/NgZone

The most common use of this service is to optimize performance when starting a work consisting of one or more asynchronous tasks that don't require UI updates or error handling to be handled by Angular. Such tasks can be kicked off via runOutsideAngular and if needed, these tasks can reenter the Angular zone via run. https://angular.io/api/core/NgZone

关键部分是运行"功能.您可以注入NgZone并将值更新放入NgZone对象的运行回调中:

The key part is the "run" function. You could inject NgZone and put your value update in the run callback of the NgZone object:

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';
      });
      }
   );
}

根据答案,这将导致整个应用程序检测到更改,而您的ChangeDetectorRef.detectChanges方法仅会检测组件及其后代中的更改.

According to this answer, it would cause the whole application to detect changes, whereas your ChangeDetectorRef.detectChanges approach would only detect changes in your component and it's descendants.

2个RxJS

另一种方法是使用rxjs更新视图.首次订阅 ReplaySubject 时,它将为您提供最新的价值. BehaviorSubject 基本相同,但允许您定义默认值(在您的示例中很有意义,但不一定一直都是正确的选择).在此初始发射之后,基本上是一个正常的重放主题:

Another way would be to use rxjs to update the view. When you first subscribe to a ReplaySubject, it will give you the latest value. A BehaviorSubject is basically the same, but allows you to define a default value (makes sense in your example, but does not necessary be the right choice all the time). After this initial emission is it basically a normal Replay Subject:

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 管道订阅主题:

In your view, you could subscribe to the Subject using the async pipe:

{{testEmitter$ | async}}

3将代码提取到新组件

如果将字符串提交给另一个组件,则该字符串也会被更新.您将必须在新组件中使用@Input()选择器.

If you submit the string to another component, it will also be updated. You would have to use the @Input() selector in the new component.

因此,新组件具有如下代码:

So the new component has code like this:

@Input() testVariable = '';

并且像以前一样,在HTML中分配了testVariable并带有卷曲的刹车.

And the testVariable is assigned in the HTML like before with curly brakets.

然后,您可以在父HTML视图中将父元素的变量传递给子元素:

In the parent HTML View you can then pass the variable of the parentelement to the child element:

<app-child [testVariable]="testVariable"></app-child>

这样,您将进入Angular区域.

This way you are in the Angular zone.

4个人偏好

我个人的喜好是使用rxjs或组件方式.对我来说,使用detectChanges或NGZone更难受.

My personal preference is to use the rxjs or the component way. Using detectChanges oder NGZone feels more hacky to me.

这篇关于在订阅中更改变量后,Angular 6 View不会更新的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆