Angular 2 runOutsideAngular仍然会更改UI

13 javascript angular

根据我的理解runOutsideAngular(),如果我需要运行一些不会触发Angular变化检测的东西,我需要使用这个函数.但是,我的代码不起作用; 当我单击按钮时,UI正在变化,数字为2.

@Component({selector: 'my-cmp', 
template: `<h1>{{num}}</h1>
           <button (click)="onClick()">Change number</button>`})

class MyComponent implements OnChanges {

  num = 1; 
  constructor(private _ngZone: NgZone ) {

  }

  onClick() {
    this._ngZone.runOutsideAngular(() => {
      this.num = 2;
    }}));
  }
}
Run Code Online (Sandbox Code Playgroud)

Gün*_*uer 13

如果有任何事情导致更改检测,并且绑定事件(click)="onClick()"确实会导致更改检测,则Angular将检测更改.

runOutsideAngular 并不意味着Angular不会看到更改,它只意味着以这种方式运行的代码不会导致更改检测,但由于click事件已经发生,因此在您的示例中它没有意义.


yur*_*zui 6

如果您想阻止更改检测,那么您可以

1)订阅ngZone.onMicrotaskEmpty如下:

import { NgZone, ChangeDetectorRef } from '@angular/core';
import 'rxjs/add/operator/first';

...
export class MyComponent {
  constructor(private ngZone: NgZone, private cdRef: ChangeDetectorRef) {}

  onClick() {
    // to do something

    this.cdRef.detach();
    this.ngZone.onMicrotaskEmpty.first().subscribe(() => {
      // reattach changeDetector after application.tick()
      this.cdRef.reattach();
    });
  }
}
Run Code Online (Sandbox Code Playgroud)

此处理程序将在之后运行 Application.tick

另见Plunker示例

2)使用这样的自定义指令:

@Directive({
  selector: '[outSideEventHandler]'
})
class OutSideEventHandlerDirective {
  private handler: Function;

  @Input() event: string = 'click'; // pass desired event
  @Output('outSideEventHandler') emitter = new EventEmitter();

  constructor(private ngZone: NgZone, private elRef: ElementRef) {}

  ngOnInit() {
    this.ngZone.runOutsideAngular(() => {
      this.handler = $event => this.emitter.emit($event);
      this.elRef.nativeElement.addEventListener(this.event, this.handler);
    });
  }

  ngOnDestory() {
    this.elRef.nativeElement.removeEventListener(this.event, this.handler);
  }
}
Run Code Online (Sandbox Code Playgroud)

然后在模板中你可以写:

<button (outSideEventHandler)="onClick()">Click outside zone</button>
Run Code Online (Sandbox Code Playgroud)

要么

<button event="mousedown" (outSideEventHandler)="onClick()">Click outside zone</button>
Run Code Online (Sandbox Code Playgroud)

Plunker

3)编写自定义DOM事件处理程序,如本文所述.

其他解决方案见:


wat*_*HUN 6

[简而言之]您需要在当前代码中更改一行

onClick() {
    this._ngZone.runOutsideAngular(() => {
        setTimeout(()=>this.num = 2,0); // instead of this.num = 2;
    }}));
  }
Run Code Online (Sandbox Code Playgroud)

现在,如果单击上<button>,this.num将成为2,但您将看不到UI中的任何更改(视图和模型之间的临时不一致)

[说明]没有runOutsideAngular(),异步函数喜欢addEventListener()setTimeout()表现不同(猴子修补).他们的回调将在运行用户代码后尝试使用Angular更新UI.例如,您可以(click)="onClick()"视为:

addEventListener("click",function modifiedCallback(){
    onClick();
    updateUIifModelChanges(); //call to Angular
})
Run Code Online (Sandbox Code Playgroud)

为了不触发UI更新,我们需要满足以下两个条件:

  1. 修改函数中的模型onClick(所以,在里面修改setTimeout())
  2. 当模型确实修改,也不能调用updateUIifModelChanges(调用setTimeout()runOutsideAngular)

[更多]原因,我给出的解释是一个非常非常简化的版本.setTimeout()具有相同的功能签名,无论它是否在内部运行runOutsideAngular().它表现不同的原因是因为它在不同的区域中运行


den*_*try 5

使用ngZone.run比解决方案好一点,setTimeout因为它使用角度特定功能。Run 旨在在ngZone.runOutsideAngular函数内使用。

来自文档:

通过 run 运行函数允许您从在 Angular 区域之外执行的任务(通常通过 {@link #runOutsideAngular} 启动)重新进入 Angular 区域。

这实际上是一个非常实际的示例,例如一个按钮将数字加一,但仅在数字为偶数时触发更改检测。

    @Component({selector: 'my-cmp', 
    template: `<h1>{{num}}</h1>
               <button (click)="onClick()">Change number</button>`})

    class MyComponent implements OnChanges {

      num = 1; 
      constructor(private _ngZone: NgZone ) {

      }

      onClick() {
        this._ngZone.runOutsideAngular(() => {
          if(this.num % 2 === 0){
              // modifying the state here wont trigger change.
              this.num++;
          } else{
            this._ngZone.run(() => {
                this.num++;
            })
          }
        
        }}));
      }
    }
Run Code Online (Sandbox Code Playgroud)

  • 我认为这不起作用,因为 `runOutsideAngular` 是*同步*的。回调会立即调用,即在“onClick”返回之前。更改检测始终在“(click)”事件之后触发,因此您的代码在实践中与OP中的代码没有什么不同。这就是为什么(工作)示例在“runOutsideAngular”中调用“setTimeout”,因为在“onClick”返回之前所做的任何更改都将被检测到,无论它们是在哪个区域中进行的。 (3认同)