Angular 2 runOutsideAngular仍会更改UI

时间:2016-10-28 08:02:02

标签: 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;
    }}));
  }
}

6 个答案:

答案 0 :(得分:13)

如果有任何事情导致更改检测,并且像(click)="onClick()"这样的绑定事件确实会导致更改检测,那么Angular将检测到更改。

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

答案 1 :(得分:6)

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

1)订阅bcda,如下所示:

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

之后运行

另请参阅 Plunker Example

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

Application.tick

然后在模板中你可以写:

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

<button (outSideEventHandler)="onClick()">Click outside zone</button>

<强> Plunker

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

其他解决方案请点击此处:

答案 2 :(得分:6)

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

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

现在,如果您点击<button>this.num将成为2,但您不会看到任何更改(视图和模型之间的暂时不一致)< / p>

[说明] 没有runOutsideAngular()addEventListener()setTimeout()等异步函数的行为方式不同(monkey patched)。他们的回调将在运行用户代码后尝试使用Angular更新UI。 例如,您可以将(click)="onClick()"视为:

addEventListener("click",function modifiedCallback(){
    onClick();
    updateUIifModelChanges(); //call to Angular
})

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

  1. 修改函数onClick中的模型(因此,在setTimeout()内修改)
  2. 当模型确实被修改时,调用updateUIifModelChanges(在setTimeout()内调用runOutsideAngular
  3. [更多] 的原因,我给出的解释是一个非常非常简化的版本。 setTimeout()具有相同的函数签名,无论它是否在runOutsideAngular()内运行。它表现不同的原因是因为它在不同的Zone

    中运行

答案 3 :(得分:4)

使用ngZone.runsetTimeout解决方案要好一些,因为它使用了角度特定的功能。运行意味着在ngZone.runOutsideAngular函数中使用。

来自文档:

  

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

这实际上是一个非常实用的例子,比如说一个按钮会将数字增加一个,但只有当数字是偶数时才触发更改检测。

&#13;
&#13;
    @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++;
            })
          }
        
        }}));
      }
    }
&#13;
&#13;
&#13;

答案 4 :(得分:0)

...
constructor(
        private ngZone: NgZone
){
    ngZone.runOutsideAngular(() => {
        setInterval(()=>{
            this.num= new Date().Format('yyyy-MM-dd HH:mm:ss');
        },1000);
    });
}
...

答案 5 :(得分:0)

这就是我试图检查 insideAngular 和 OutsideAngular 的区别

constructor(private zone: NgZone) { }
    
        setProgressOutsideAngular() {
            this.zone.runOutsideAngular(() => {
                setInterval(() => { ++this.progress, console.log(this.progress) }, 500)
            })
        }
    
        setProgressInsideAngular() {
            this.zone.run(() => setInterval(() => { ++this.progress, console.log(this.progress) }, 500))            
    
        }