revert: feat: add a flag in bootstrap to enable coalesce event change detection to improve performance (#30533) (#33230)
This reverts commit 21c1e14385
.
PR Close #33230
This commit is contained in:
parent
d192a7b47a
commit
082aed6e46
|
@ -21,7 +21,7 @@
|
||||||
"master": {
|
"master": {
|
||||||
"uncompressed": {
|
"uncompressed": {
|
||||||
"runtime": 1440,
|
"runtime": 1440,
|
||||||
"main": 125178,
|
"main": 123904,
|
||||||
"polyfills": 45340
|
"polyfills": 45340
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,23 +14,6 @@ var __global = "undefined" !== typeof global && global;
|
||||||
|
|
||||||
var _global = __globalThis || __global || __window || __self;
|
var _global = __globalThis || __global || __window || __self;
|
||||||
|
|
||||||
function getNativeRequestAnimationFrame() {
|
|
||||||
var nativeRequestAnimationFrame = _global["requestAnimationFrame"];
|
|
||||||
var nativeCancelAnimationFrame = _global["cancelAnimationFrame"];
|
|
||||||
if ("undefined" !== typeof Zone && nativeRequestAnimationFrame && nativeCancelAnimationFrame) {
|
|
||||||
var unpatchedRequestAnimationFrame = nativeRequestAnimationFrame[Zone.__symbol__("OriginalDelegate")];
|
|
||||||
if (unpatchedRequestAnimationFrame) nativeRequestAnimationFrame = unpatchedRequestAnimationFrame;
|
|
||||||
var unpatchedCancelAnimationFrame = nativeCancelAnimationFrame[Zone.__symbol__("OriginalDelegate")];
|
|
||||||
if (unpatchedCancelAnimationFrame) nativeCancelAnimationFrame = unpatchedCancelAnimationFrame;
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
nativeRequestAnimationFrame: nativeRequestAnimationFrame,
|
|
||||||
nativeCancelAnimationFrame: nativeCancelAnimationFrame
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
var nativeRequestAnimationFrame = getNativeRequestAnimationFrame().nativeRequestAnimationFrame;
|
|
||||||
|
|
||||||
if (ngDevMode) _global.$localize = _global.$localize || function() {
|
if (ngDevMode) _global.$localize = _global.$localize || function() {
|
||||||
throw new Error("It looks like your application or one of its dependencies is using i18n.\n" + "Angular 9 introduced a global `$localize()` function that needs to be loaded.\n" + "Please add `import '@angular/localize/init';` to your polyfills.ts file.");
|
throw new Error("It looks like your application or one of its dependencies is using i18n.\n" + "Angular 9 introduced a global `$localize()` function that needs to be loaded.\n" + "Please add `import '@angular/localize/init';` to your polyfills.ts file.");
|
||||||
};
|
};
|
||||||
|
|
|
@ -219,27 +219,6 @@ export interface BootstrapOptions {
|
||||||
* - `noop` - Use `NoopNgZone` which does nothing.
|
* - `noop` - Use `NoopNgZone` which does nothing.
|
||||||
*/
|
*/
|
||||||
ngZone?: NgZone|'zone.js'|'noop';
|
ngZone?: NgZone|'zone.js'|'noop';
|
||||||
|
|
||||||
/**
|
|
||||||
* Optionally specify coalescing event change detections or not.
|
|
||||||
* Consider the following case.
|
|
||||||
*
|
|
||||||
* <div (click)="doSomething()">
|
|
||||||
* <button (click)="doSomethingElse()"></button>
|
|
||||||
* </div>
|
|
||||||
*
|
|
||||||
* When button is clicked, because of the event bubbling, both
|
|
||||||
* event handlers will be called and 2 change detections will be
|
|
||||||
* triggered. We can colesce such kind of events to only trigger
|
|
||||||
* change detection only once.
|
|
||||||
*
|
|
||||||
* By default, this option will be false. So the events will not be
|
|
||||||
* colesced and the change detection will be triggered multiple times.
|
|
||||||
* And if this option be set to true, the change detection will be
|
|
||||||
* triggered async by scheduling a animation frame. So in the case above,
|
|
||||||
* the change detection will only be trigged once.
|
|
||||||
*/
|
|
||||||
ngZoneEventCoalescing?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -290,8 +269,7 @@ export class PlatformRef {
|
||||||
// So we create a mini parent injector that just contains the new NgZone and
|
// So we create a mini parent injector that just contains the new NgZone and
|
||||||
// pass that as parent to the NgModuleFactory.
|
// pass that as parent to the NgModuleFactory.
|
||||||
const ngZoneOption = options ? options.ngZone : undefined;
|
const ngZoneOption = options ? options.ngZone : undefined;
|
||||||
const ngZoneEventCoalescing = (options && options.ngZoneEventCoalescing) || false;
|
const ngZone = getNgZone(ngZoneOption);
|
||||||
const ngZone = getNgZone(ngZoneOption, ngZoneEventCoalescing);
|
|
||||||
const providers: StaticProvider[] = [{provide: NgZone, useValue: ngZone}];
|
const providers: StaticProvider[] = [{provide: NgZone, useValue: ngZone}];
|
||||||
// Attention: Don't use ApplicationRef.run here,
|
// Attention: Don't use ApplicationRef.run here,
|
||||||
// as we want to be sure that all possible constructor calls are inside `ngZone.run`!
|
// as we want to be sure that all possible constructor calls are inside `ngZone.run`!
|
||||||
|
@ -387,17 +365,14 @@ export class PlatformRef {
|
||||||
get destroyed() { return this._destroyed; }
|
get destroyed() { return this._destroyed; }
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNgZone(
|
function getNgZone(ngZoneOption?: NgZone | 'zone.js' | 'noop'): NgZone {
|
||||||
ngZoneOption: NgZone | 'zone.js' | 'noop' | undefined, ngZoneEventCoalescing: boolean): NgZone {
|
|
||||||
let ngZone: NgZone;
|
let ngZone: NgZone;
|
||||||
|
|
||||||
if (ngZoneOption === 'noop') {
|
if (ngZoneOption === 'noop') {
|
||||||
ngZone = new NoopNgZone();
|
ngZone = new NoopNgZone();
|
||||||
} else {
|
} else {
|
||||||
ngZone = (ngZoneOption === 'zone.js' ? undefined : ngZoneOption) || new NgZone({
|
ngZone = (ngZoneOption === 'zone.js' ? undefined : ngZoneOption) ||
|
||||||
enableLongStackTrace: isDevMode(),
|
new NgZone({enableLongStackTrace: isDevMode()});
|
||||||
shouldCoalesceEventChangeDetection: ngZoneEventCoalescing
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
return ngZone;
|
return ngZone;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
import {global} from './global';
|
|
||||||
|
|
||||||
export function getNativeRequestAnimationFrame() {
|
|
||||||
let nativeRequestAnimationFrame: (callback: FrameRequestCallback) => number =
|
|
||||||
global['requestAnimationFrame'];
|
|
||||||
let nativeCancelAnimationFrame: (handle: number) => void = global['cancelAnimationFrame'];
|
|
||||||
if (typeof Zone !== 'undefined' && nativeRequestAnimationFrame && nativeCancelAnimationFrame) {
|
|
||||||
// use unpatched version of requestAnimationFrame(native delegate) if possible
|
|
||||||
// to avoid another Change detection
|
|
||||||
const unpatchedRequestAnimationFrame =
|
|
||||||
(nativeRequestAnimationFrame as any)[(Zone as any).__symbol__('OriginalDelegate')];
|
|
||||||
if (unpatchedRequestAnimationFrame) {
|
|
||||||
nativeRequestAnimationFrame = unpatchedRequestAnimationFrame;
|
|
||||||
}
|
|
||||||
const unpatchedCancelAnimationFrame =
|
|
||||||
(nativeCancelAnimationFrame as any)[(Zone as any).__symbol__('OriginalDelegate')];
|
|
||||||
if (unpatchedCancelAnimationFrame) {
|
|
||||||
nativeCancelAnimationFrame = unpatchedCancelAnimationFrame;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {nativeRequestAnimationFrame, nativeCancelAnimationFrame};
|
|
||||||
}
|
|
|
@ -7,9 +7,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {EventEmitter} from '../event_emitter';
|
import {EventEmitter} from '../event_emitter';
|
||||||
import {global} from '../util/global';
|
|
||||||
import {getNativeRequestAnimationFrame} from '../util/raf';
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An injectable service for executing work inside or outside of the Angular zone.
|
* An injectable service for executing work inside or outside of the Angular zone.
|
||||||
|
@ -86,11 +83,8 @@ import {getNativeRequestAnimationFrame} from '../util/raf';
|
||||||
* @publicApi
|
* @publicApi
|
||||||
*/
|
*/
|
||||||
export class NgZone {
|
export class NgZone {
|
||||||
readonly hasPendingZoneMicrotasks: boolean = false;
|
|
||||||
readonly lastRequestAnimationFrameId: number = -1;
|
|
||||||
readonly shouldCoalesceEventChangeDetection: boolean = true;
|
|
||||||
readonly hasPendingMacrotasks: boolean = false;
|
|
||||||
readonly hasPendingMicrotasks: boolean = false;
|
readonly hasPendingMicrotasks: boolean = false;
|
||||||
|
readonly hasPendingMacrotasks: boolean = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether there are no outstanding microtasks or macrotasks.
|
* Whether there are no outstanding microtasks or macrotasks.
|
||||||
|
@ -121,8 +115,7 @@ export class NgZone {
|
||||||
*/
|
*/
|
||||||
readonly onError: EventEmitter<any> = new EventEmitter(false);
|
readonly onError: EventEmitter<any> = new EventEmitter(false);
|
||||||
|
|
||||||
|
constructor({enableLongStackTrace = false}) {
|
||||||
constructor({enableLongStackTrace = false, shouldCoalesceEventChangeDetection = false}) {
|
|
||||||
if (typeof Zone == 'undefined') {
|
if (typeof Zone == 'undefined') {
|
||||||
throw new Error(`In this configuration Angular requires Zone.js`);
|
throw new Error(`In this configuration Angular requires Zone.js`);
|
||||||
}
|
}
|
||||||
|
@ -145,7 +138,6 @@ export class NgZone {
|
||||||
self._inner = self._inner.fork((Zone as any)['longStackTraceZoneSpec']);
|
self._inner = self._inner.fork((Zone as any)['longStackTraceZoneSpec']);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.shouldCoalesceEventChangeDetection = shouldCoalesceEventChangeDetection;
|
|
||||||
forkInnerZoneWithAngularBehavior(self);
|
forkInnerZoneWithAngularBehavior(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,19 +221,16 @@ export class NgZone {
|
||||||
|
|
||||||
function noop() {}
|
function noop() {}
|
||||||
const EMPTY_PAYLOAD = {};
|
const EMPTY_PAYLOAD = {};
|
||||||
const {nativeRequestAnimationFrame} = getNativeRequestAnimationFrame();
|
|
||||||
|
|
||||||
interface NgZonePrivate extends NgZone {
|
interface NgZonePrivate extends NgZone {
|
||||||
_outer: Zone;
|
_outer: Zone;
|
||||||
_inner: Zone;
|
_inner: Zone;
|
||||||
_nesting: number;
|
_nesting: number;
|
||||||
_hasPendingMicrotasks: boolean;
|
|
||||||
|
|
||||||
hasPendingMacrotasks: boolean;
|
|
||||||
hasPendingMicrotasks: boolean;
|
hasPendingMicrotasks: boolean;
|
||||||
lastRequestAnimationFrameId: number;
|
hasPendingMacrotasks: boolean;
|
||||||
isStable: boolean;
|
isStable: boolean;
|
||||||
shouldCoalesceEventChangeDetection: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkStable(zone: NgZonePrivate) {
|
function checkStable(zone: NgZonePrivate) {
|
||||||
|
@ -262,35 +251,16 @@ function checkStable(zone: NgZonePrivate) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function delayChangeDetectionForEvents(zone: NgZonePrivate) {
|
|
||||||
if (zone.lastRequestAnimationFrameId !== -1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
zone.lastRequestAnimationFrameId = nativeRequestAnimationFrame.call(global, () => {
|
|
||||||
zone.lastRequestAnimationFrameId = -1;
|
|
||||||
updateMicroTaskStatus(zone);
|
|
||||||
checkStable(zone);
|
|
||||||
});
|
|
||||||
updateMicroTaskStatus(zone);
|
|
||||||
}
|
|
||||||
|
|
||||||
function forkInnerZoneWithAngularBehavior(zone: NgZonePrivate) {
|
function forkInnerZoneWithAngularBehavior(zone: NgZonePrivate) {
|
||||||
const delayChangeDetectionForEventsDelegate = () => { delayChangeDetectionForEvents(zone); };
|
|
||||||
const maybeDelayChangeDetection = !!zone.shouldCoalesceEventChangeDetection &&
|
|
||||||
nativeRequestAnimationFrame && delayChangeDetectionForEventsDelegate;
|
|
||||||
zone._inner = zone._inner.fork({
|
zone._inner = zone._inner.fork({
|
||||||
name: 'angular',
|
name: 'angular',
|
||||||
properties:
|
properties: <any>{'isAngularZone': true},
|
||||||
<any>{'isAngularZone': true, 'maybeDelayChangeDetection': maybeDelayChangeDetection},
|
|
||||||
onInvokeTask: (delegate: ZoneDelegate, current: Zone, target: Zone, task: Task, applyThis: any,
|
onInvokeTask: (delegate: ZoneDelegate, current: Zone, target: Zone, task: Task, applyThis: any,
|
||||||
applyArgs: any): any => {
|
applyArgs: any): any => {
|
||||||
try {
|
try {
|
||||||
onEnter(zone);
|
onEnter(zone);
|
||||||
return delegate.invokeTask(target, task, applyThis, applyArgs);
|
return delegate.invokeTask(target, task, applyThis, applyArgs);
|
||||||
} finally {
|
} finally {
|
||||||
if (maybeDelayChangeDetection && task.type === 'eventTask') {
|
|
||||||
maybeDelayChangeDetection();
|
|
||||||
}
|
|
||||||
onLeave(zone);
|
onLeave(zone);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -313,8 +283,7 @@ function forkInnerZoneWithAngularBehavior(zone: NgZonePrivate) {
|
||||||
// We are only interested in hasTask events which originate from our zone
|
// We are only interested in hasTask events which originate from our zone
|
||||||
// (A child hasTask event is not interesting to us)
|
// (A child hasTask event is not interesting to us)
|
||||||
if (hasTaskState.change == 'microTask') {
|
if (hasTaskState.change == 'microTask') {
|
||||||
zone._hasPendingMicrotasks = hasTaskState.microTask;
|
zone.hasPendingMicrotasks = hasTaskState.microTask;
|
||||||
updateMicroTaskStatus(zone);
|
|
||||||
checkStable(zone);
|
checkStable(zone);
|
||||||
} else if (hasTaskState.change == 'macroTask') {
|
} else if (hasTaskState.change == 'macroTask') {
|
||||||
zone.hasPendingMacrotasks = hasTaskState.macroTask;
|
zone.hasPendingMacrotasks = hasTaskState.macroTask;
|
||||||
|
@ -330,15 +299,6 @@ function forkInnerZoneWithAngularBehavior(zone: NgZonePrivate) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateMicroTaskStatus(zone: NgZonePrivate) {
|
|
||||||
if (zone._hasPendingMicrotasks ||
|
|
||||||
(zone.shouldCoalesceEventChangeDetection && zone.lastRequestAnimationFrameId !== -1)) {
|
|
||||||
zone.hasPendingMicrotasks = true;
|
|
||||||
} else {
|
|
||||||
zone.hasPendingMicrotasks = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onEnter(zone: NgZonePrivate) {
|
function onEnter(zone: NgZonePrivate) {
|
||||||
zone._nesting++;
|
zone._nesting++;
|
||||||
if (zone.isStable) {
|
if (zone.isStable) {
|
||||||
|
@ -357,8 +317,6 @@ function onLeave(zone: NgZonePrivate) {
|
||||||
* to framework to perform rendering.
|
* to framework to perform rendering.
|
||||||
*/
|
*/
|
||||||
export class NoopNgZone implements NgZone {
|
export class NoopNgZone implements NgZone {
|
||||||
readonly hasPendingZoneMicrotasks: boolean = false;
|
|
||||||
readonly lastRequestAnimationFrameId = -1;
|
|
||||||
readonly hasPendingMicrotasks: boolean = false;
|
readonly hasPendingMicrotasks: boolean = false;
|
||||||
readonly hasPendingMacrotasks: boolean = false;
|
readonly hasPendingMacrotasks: boolean = false;
|
||||||
readonly isStable: boolean = true;
|
readonly isStable: boolean = true;
|
||||||
|
@ -366,7 +324,6 @@ export class NoopNgZone implements NgZone {
|
||||||
readonly onMicrotaskEmpty: EventEmitter<any> = new EventEmitter();
|
readonly onMicrotaskEmpty: EventEmitter<any> = new EventEmitter();
|
||||||
readonly onStable: EventEmitter<any> = new EventEmitter();
|
readonly onStable: EventEmitter<any> = new EventEmitter();
|
||||||
readonly onError: EventEmitter<any> = new EventEmitter();
|
readonly onError: EventEmitter<any> = new EventEmitter();
|
||||||
readonly shouldCoalesceEventChangeDetection: boolean = false;
|
|
||||||
|
|
||||||
run(fn: (...args: any[]) => any, applyThis?: any, applyArgs?: any): any {
|
run(fn: (...args: any[]) => any, applyThis?: any, applyArgs?: any): any {
|
||||||
return fn.apply(applyThis, applyArgs);
|
return fn.apply(applyThis, applyArgs);
|
||||||
|
|
|
@ -95,7 +95,7 @@ const ProxyZoneSpec: {assertPresent: () => void} = (Zone as any)['ProxyZoneSpec'
|
||||||
resolvedPromise.then((_) => { throw new Error('async'); });
|
resolvedPromise.then((_) => { throw new Error('async'); });
|
||||||
flushMicrotasks();
|
flushMicrotasks();
|
||||||
})();
|
})();
|
||||||
}).toThrow();
|
}).toThrowError(/Uncaught \(in promise\): Error: async/);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should complain if a test throws an exception', () => {
|
it('should complain if a test throws an exception', () => {
|
||||||
|
|
|
@ -16,7 +16,7 @@ import {EventEmitter, Injectable, NgZone} from '@angular/core';
|
||||||
export class MockNgZone extends NgZone {
|
export class MockNgZone extends NgZone {
|
||||||
onStable: EventEmitter<any> = new EventEmitter(false);
|
onStable: EventEmitter<any> = new EventEmitter(false);
|
||||||
|
|
||||||
constructor() { super({enableLongStackTrace: false, shouldCoalesceEventChangeDetection: false}); }
|
constructor() { super({enableLongStackTrace: false}); }
|
||||||
|
|
||||||
run(fn: Function): any { return fn(); }
|
run(fn: Function): any { return fn(); }
|
||||||
|
|
||||||
|
|
|
@ -402,8 +402,7 @@ export class TestBedViewEngine implements TestBed {
|
||||||
overrideComponentView(component, compFactory);
|
overrideComponentView(component, compFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
const ngZone =
|
const ngZone = new NgZone({enableLongStackTrace: true});
|
||||||
new NgZone({enableLongStackTrace: true, shouldCoalesceEventChangeDetection: false});
|
|
||||||
const providers: StaticProvider[] = [{provide: NgZone, useValue: ngZone}];
|
const providers: StaticProvider[] = [{provide: NgZone, useValue: ngZone}];
|
||||||
const ngZoneInjector = Injector.create({
|
const ngZoneInjector = Injector.create({
|
||||||
providers: providers,
|
providers: providers,
|
||||||
|
|
|
@ -20,6 +20,7 @@ import {createMouseEvent, el} from '../../../testing/src/browser_util';
|
||||||
let zone: NgZone;
|
let zone: NgZone;
|
||||||
|
|
||||||
describe('EventManager', () => {
|
describe('EventManager', () => {
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
doc = getDOM().supportsDOMEvents() ? document : getDOM().createHtmlDocument();
|
doc = getDOM().supportsDOMEvents() ? document : getDOM().createHtmlDocument();
|
||||||
zone = new NgZone({});
|
zone = new NgZone({});
|
||||||
|
@ -295,7 +296,7 @@ import {createMouseEvent, el} from '../../../testing/src/browser_util';
|
||||||
expect(receivedEvents).toEqual([]);
|
expect(receivedEvents).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should run blackListedEvents handler outside of ngZone', () => {
|
it('should run blockListedEvents handler outside of ngZone', () => {
|
||||||
const Zone = (window as any)['Zone'];
|
const Zone = (window as any)['Zone'];
|
||||||
const element = el('<div><div></div></div>');
|
const element = el('<div><div></div></div>');
|
||||||
doc.body.appendChild(element);
|
doc.body.appendChild(element);
|
||||||
|
@ -311,45 +312,13 @@ import {createMouseEvent, el} from '../../../testing/src/browser_util';
|
||||||
let remover = manager.addEventListener(element, 'scroll', handler);
|
let remover = manager.addEventListener(element, 'scroll', handler);
|
||||||
getDOM().dispatchEvent(element, dispatchedEvent);
|
getDOM().dispatchEvent(element, dispatchedEvent);
|
||||||
expect(receivedEvent).toBe(dispatchedEvent);
|
expect(receivedEvent).toBe(dispatchedEvent);
|
||||||
expect(receivedZone.name).not.toEqual('angular');
|
expect(receivedZone.name).toBe(Zone.root.name);
|
||||||
|
|
||||||
receivedEvent = null;
|
receivedEvent = null;
|
||||||
remover && remover();
|
remover && remover();
|
||||||
getDOM().dispatchEvent(element, dispatchedEvent);
|
getDOM().dispatchEvent(element, dispatchedEvent);
|
||||||
expect(receivedEvent).toBe(null);
|
expect(receivedEvent).toBe(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should only trigger one Change detection when bubbling', (done: DoneFn) => {
|
|
||||||
doc = getDOM().supportsDOMEvents() ? document : getDOM().createHtmlDocument();
|
|
||||||
zone = new NgZone({shouldCoalesceEventChangeDetection: true});
|
|
||||||
domEventPlugin = new DomEventsPlugin(doc, zone, null);
|
|
||||||
const element = el('<div></div>');
|
|
||||||
const child = el('<div></div>');
|
|
||||||
element.appendChild(child);
|
|
||||||
doc.body.appendChild(element);
|
|
||||||
const dispatchedEvent = createMouseEvent('click');
|
|
||||||
let receivedEvents: any = [];
|
|
||||||
let stables: any = [];
|
|
||||||
const handler = (e: any) => { receivedEvents.push(e); };
|
|
||||||
const manager = new EventManager([domEventPlugin], zone);
|
|
||||||
let removerChild: any;
|
|
||||||
let removerParent: any;
|
|
||||||
|
|
||||||
zone.run(() => {
|
|
||||||
removerChild = manager.addEventListener(child, 'click', handler);
|
|
||||||
removerParent = manager.addEventListener(element, 'click', handler);
|
|
||||||
});
|
|
||||||
zone.onStable.subscribe((isStable: any) => { stables.push(isStable); });
|
|
||||||
getDOM().dispatchEvent(child, dispatchedEvent);
|
|
||||||
requestAnimationFrame(() => {
|
|
||||||
expect(receivedEvents.length).toBe(2);
|
|
||||||
expect(stables.length).toBe(1);
|
|
||||||
|
|
||||||
removerChild && removerChild();
|
|
||||||
removerParent && removerParent();
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
@ -363,12 +332,12 @@ class FakeEventManagerPlugin extends EventManagerPlugin {
|
||||||
|
|
||||||
addEventListener(element: any, eventName: string, handler: Function) {
|
addEventListener(element: any, eventName: string, handler: Function) {
|
||||||
this.eventHandler[eventName] = handler;
|
this.eventHandler[eventName] = handler;
|
||||||
return () => { delete this.eventHandler[eventName]; };
|
return () => { delete (this.eventHandler[eventName]); };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class FakeNgZone extends NgZone {
|
class FakeNgZone extends NgZone {
|
||||||
constructor() { super({enableLongStackTrace: false, shouldCoalesceEventChangeDetection: true}); }
|
constructor() { super({enableLongStackTrace: false}); }
|
||||||
run<T>(fn: (...args: any[]) => T, applyThis?: any, applyArgs?: any[]): T { return fn(); }
|
run<T>(fn: (...args: any[]) => T, applyThis?: any, applyArgs?: any[]): T { return fn(); }
|
||||||
runOutsideAngular(fn: Function) { return fn(); }
|
runOutsideAngular(fn: Function) { return fn(); }
|
||||||
}
|
}
|
||||||
|
|
|
@ -175,7 +175,7 @@ export function stringifyElement(el: any /** TODO #9100 */): string {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createNgZone(): NgZone {
|
export function createNgZone(): NgZone {
|
||||||
return new NgZone({enableLongStackTrace: true, shouldCoalesceEventChangeDetection: false});
|
return new NgZone({enableLongStackTrace: true});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isCommentNode(node: Node): boolean {
|
export function isCommentNode(node: Node): boolean {
|
||||||
|
|
|
@ -624,17 +624,13 @@ export declare class NgProbeToken {
|
||||||
export declare class NgZone {
|
export declare class NgZone {
|
||||||
readonly hasPendingMacrotasks: boolean;
|
readonly hasPendingMacrotasks: boolean;
|
||||||
readonly hasPendingMicrotasks: boolean;
|
readonly hasPendingMicrotasks: boolean;
|
||||||
readonly hasPendingZoneMicrotasks: boolean;
|
|
||||||
readonly isStable: boolean;
|
readonly isStable: boolean;
|
||||||
readonly lastRequestAnimationFrameId: number;
|
|
||||||
readonly onError: EventEmitter<any>;
|
readonly onError: EventEmitter<any>;
|
||||||
readonly onMicrotaskEmpty: EventEmitter<any>;
|
readonly onMicrotaskEmpty: EventEmitter<any>;
|
||||||
readonly onStable: EventEmitter<any>;
|
readonly onStable: EventEmitter<any>;
|
||||||
readonly onUnstable: EventEmitter<any>;
|
readonly onUnstable: EventEmitter<any>;
|
||||||
readonly shouldCoalesceEventChangeDetection: boolean;
|
constructor({ enableLongStackTrace }: {
|
||||||
constructor({ enableLongStackTrace, shouldCoalesceEventChangeDetection }: {
|
|
||||||
enableLongStackTrace?: boolean | undefined;
|
enableLongStackTrace?: boolean | undefined;
|
||||||
shouldCoalesceEventChangeDetection?: boolean | undefined;
|
|
||||||
});
|
});
|
||||||
run<T>(fn: (...args: any[]) => T, applyThis?: any, applyArgs?: any[]): T;
|
run<T>(fn: (...args: any[]) => T, applyThis?: any, applyArgs?: any[]): T;
|
||||||
runGuarded<T>(fn: (...args: any[]) => T, applyThis?: any, applyArgs?: any[]): T;
|
runGuarded<T>(fn: (...args: any[]) => T, applyThis?: any, applyArgs?: any[]): T;
|
||||||
|
|
Loading…
Reference in New Issue