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

查看:27
本文介绍了更改订阅中的变量后,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?

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

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 只会更新视图,如果您更改Angular zone"中的数据.您示例中的异步调用不符合此条件.但是如果你愿意,你可以把它放在 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 个角区

此服务最常见的用途是在启动由一个或多个异步任务组成的工作时优化性能,这些任务不需要 UI 更新或错误处理由 Angular 处理.此类任务可以通过 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';
      });
      }
   );
}

根据 this 答案,它会导致整个应用程序检测到更改,而您的 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 = '';

并且 testVariable 像以前一样在 HTML 中使用卷曲刹车进行分配.

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 或组件方式.在 NGZone 上使用 detectChanges 对我来说感觉更麻烦.

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天全站免登陆