我在尝试在Angular 2中的(click)
事件回调函数中执行某些逻辑时遇到了重大麻烦,而没有触发更改检测。
为什么我不想触发变更检测
回调函数执行简单的动画滚动到顶部。它不会影响任何其他组件,因此我不需要在每个组件中进行变化检测,事实上我真的不想让它发射!由于变化检测在每个组件中都会触发,因此动画在移动设备上的表现非常差。
我尝试了什么
我知道我可以使用
在区域之外运行代码this._zone.runOutsideAngular(() => {
someFunction();
})
当您要在区域外运行的代码未被click
,keyup
,keydown
事件等调用时,这很有效。
因为我的组件看起来像......
<button (click)="doThing()">Do that thing outside the zone!</button>
doThing() {
this._zone.runOutsideAngular(() => {
myFunction();
})
}
... myFunction
将在区域外运行,但click
事件将触发更改检测。
我已经在尽可能多的组件中实施了OnPush
更改检测策略。
在此处未触发更改检测非常重要,但我无法找到预防方法。
修改
请参阅此plnk,并在所有组件中将更改检测设置为OnPush
。单击子行组件按钮会在子行,行和应用程序组件中触发CD。
答案 0 :(得分:5)
如上所述,即使将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 :(得分:1)
<击>
如果您使用OnPush
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
})
然后更改检测应限于发生click事件的组件。
击>
您可以分离ChangeDetectorRef
constructor(private ref: ChangeDetectorRef, private dataProvider:DataProvider) {
ref.detach();
}
在重新连接之前,不会对此组件运行更改检测。
答案 2 :(得分:1)
我可以展示一个完整的黑客但似乎有效。
<强>子行component.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 强>