2016-09-27 87 views
3

我在嘗試執行Angular 2中的(click)事件回調函數內的某些邏輯時遇到了重大故障,而未觸發更改檢測。Angular 2單擊事件回調而不觸發更改檢測

爲什麼我不想觸發變化檢測

回調函數執行簡單的動畫滾動到頂部。它不會影響其他組件,因此我不需要在每個組件中觸發更改檢測,事實上,我真的不希望它啓動!由於變化檢測在每個組件中都會觸發,因此移動設備上的動畫性能非常差。

我已經試過

我知道我可以使用區外運行的代碼

this._zone.runOutsideAngular(() => { 
    someFunction(); 
}) 

當代碼要運行的外部不調用區這很好地工作由clickkeyup,​​事件等

因爲我的組件看起來像......

<button (click)="doThing()">Do that thing outside the zone!</button>

doThing() { 
    this._zone.runOutsideAngular(() => { 
     myFunction(); 
    }) 
} 

... myFunction將區外運行,但click事件將觸發變化檢測。

我已經在儘可能多的組件中實現了OnPush更改檢測策略。

重要的是變化檢測不會觸發在這裏,但我找不到防止它的方法。

編輯 看到這個plnk與變化檢測中的所有組件設置爲OnPush。點擊subrow組件按鈕將在子行,行和應用程序組件中觸發CD。

+0

您是否嘗試在該組件的@Component({})'裝飾器中使用'changeDetection:ChangeDetectionStrategy.OnPush'? – micronyks

回答

4

如上所述,即使設置在其中click事件發生時將OnPush仍然導致觸發變化檢測的組件。

出的現成事件處理

@Component({ 
    selector: 'test' 
    template: `<button (click)="onClick($event)">Click me</button>`, 
    changeDetection: ChangeDetectionStrategy.OnPush 
}) 
export class MyComponent { 

    onClick(e:Event) { 
     e.preventDefault(); 
     e.stopPropagation(); 

     this._zone.runOutsideAngular(() => { 
      // gets run outside of Angular but parent function still triggers change detection 
     }); 

     return false; 

     /* 
     * change detection always triggered 
     * regardless of what happens above 
     */ 
    } 

} 

大部分的時間,這可能不是一個問題,但在渲染時和某些功能的繪畫表現我走的方法至關重要完全繞過了Angular的內置事件處理。從我自己的實驗中,以下似乎是解決這個問題的最簡單和最可維護的方法。

繞道出的現成事件處理

的想法在這裏是用RxJS點擊手動綁定到我想要的任何事件,在這種情況下,使用Observable.fromEvent。此示例還說明了清理事件訂閱的做法。

import {Component, ElementRef, OnInit, OnDestroy, NgZone} from '@angular/core'; 
import {Observable} from 'rxjs/Observable'; 
import {Subscription} from 'rxjs/Subscription'; 

@Component({ 
    selector: 'test' 
    template: `<button #myButton>Click me</button>`, 
    changeDetection: ChangeDetectionStrategy.OnPush 
}) 
export class MyComponent implements OnInit { 

    private _subs:Subscription[] = []; 

    // reference to template variable 
    @ViewChild('myButton') myButton:ElementRef; 

    constructor(private _zone:NgZone){} 

    ngOnInit() { 
     this.manuallyBindToViewEvents(); 
    } 

    manuallyBindToViewEvents() { 
     this.subscribeToMyButton() 
    } 

    subscribeToMyButton() { 
     let sub:Subscription; 

     this._zone.runOutsideAngular(() => { 
      sub = Observable.fromEvent(this.myButton.nativeElement, 'click').subscribe(e => { 
       console.log('Yayyyyy! No change detection!'); 
      }) 
     }); 

     this._subs.push(sub); 
    } 

    ngOnDestroy() { 
     // clean up subscriptions 
     this._subs.forEach(sub => sub.unsubscribe()); 
    } 

} 

希望這對於其他類似情況有所幫助。

1

如果使用 OnPush

@Component({ 
    changeDetection: ChangeDetectionStrategy.OnPush, 
}) 

然後更改檢測應僅限於點擊事件發生的組件。

您可以分離ChangeDetectorRef

constructor(private ref: ChangeDetectorRef, private dataProvider:DataProvider) { 
    ref.detach(); 
    } 

變化檢測會,直到重新連接不被此組件運行。

+0

我不確定是這種情況。我會用格式不正確的plnk編輯原始帖子! – garethdn

+0

'detatch()'似乎阻止了變化檢測https://plnkr.co/edit/wVjjGhcZkqVbtsVjvZyi?p=preview。 –

+0

我也對事件嘗試了'preventDefault()'和'stopPropagation()',但這並沒有阻止變更檢測在所有組件上運行。 –

1

我可以顯示一個完整的黑客,但似乎它的工作。

子行組件。TS

constructor(private zone: NgZone, private app: ApplicationRef) {} 

rowClick() { 
    console.log('rowClick'); 
    var a = 4; 

    let originalTick = (<any>this.app).constructor.prototype.tick; 
    (<any>this.app).constructor.prototype.tick =() => console.info('tick tick tick :)'); 
    const subscriber = this.zone.onMicrotaskEmpty.subscribe({ 
    next:() => { 
     console.info('tick has been stopped. So we can return everything in its place'); 
     subscriber.unsubscribe(); 
     (<any>this.app).constructor.prototype.tick = originalTick; 
    }}); 
} 

Demo Plunker