Skip to content
This repository was archived by the owner on Feb 26, 2024. It is now read-only.

Commit dcc285a

Browse files
JiaLiPassionmhevery
authored andcommitted
fix(event): should handle event.stopImmediatePropagration (#903)
1 parent 956c729 commit dcc285a

File tree

4 files changed

+67
-7
lines changed

4 files changed

+67
-7
lines changed

lib/browser/browser.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,15 @@ import {patchTimer} from '../common/timers';
1515
import {patchClass, patchMacroTask, patchMethod, patchOnProperties, patchPrototype, zoneSymbol} from '../common/utils';
1616

1717
import {propertyPatch} from './define-property';
18-
import {eventTargetPatch} from './event-target';
18+
import {eventTargetPatch, patchEvent} from './event-target';
1919
import {propertyDescriptorPatch} from './property-descriptor';
2020
import {registerElementPatch} from './register-element';
2121

22+
Zone.__load_patch('util', (global: any, Zone: ZoneType, api: _ZonePrivate) => {
23+
api.patchOnProperties = patchOnProperties;
24+
api.patchMethod = patchMethod;
25+
});
26+
2227
Zone.__load_patch('timers', (global: any, Zone: ZoneType, api: _ZonePrivate) => {
2328
const set = 'set';
2429
const clear = 'clear';
@@ -46,6 +51,7 @@ Zone.__load_patch('blocking', (global: any, Zone: ZoneType, api: _ZonePrivate) =
4651
});
4752

4853
Zone.__load_patch('EventTarget', (global: any, Zone: ZoneType, api: _ZonePrivate) => {
54+
patchEvent(global, api);
4955
eventTargetPatch(global, api);
5056
// patch XMLHttpRequestEventTarget's addEventListener/removeEventListener
5157
const XMLHttpRequestEventTarget = (global as any)['XMLHttpRequestEventTarget'];
@@ -240,8 +246,3 @@ Zone.__load_patch('PromiseRejectionEvent', (global: any, Zone: ZoneType, api: _Z
240246
findPromiseRejectionHandler('rejectionhandled');
241247
}
242248
});
243-
244-
Zone.__load_patch('util', (global: any, Zone: ZoneType, api: _ZonePrivate) => {
245-
api.patchOnProperties = patchOnProperties;
246-
api.patchMethod = patchMethod;
247-
});

lib/browser/event-target.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {FALSE_STR, globalSources, patchEventTarget, TRUE_STR, ZONE_SYMBOL_PREFIX, zoneSymbolEventNames} from '../common/events';
9+
import {FALSE_STR, globalSources, patchEventPrototype, patchEventTarget, TRUE_STR, ZONE_SYMBOL_PREFIX, zoneSymbolEventNames} from '../common/events';
1010
import {attachOriginToPatched, isIEOrEdge, zoneSymbol} from '../common/utils';
1111

1212
import {eventNames} from './property-descriptor';
@@ -106,3 +106,7 @@ export function eventTargetPatch(_global: any, api: _ZonePrivate) {
106106

107107
return true;
108108
}
109+
110+
export function patchEvent(global: any, api: _ZonePrivate) {
111+
patchEventPrototype(global, api);
112+
}

lib/common/events.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ export const ZONE_SYMBOL_PREFIX = '__zone_symbol__';
3333

3434
const EVENT_NAME_SYMBOL_REGX = /^__zone_symbol__(\w+)(true|false)$/;
3535

36+
const IMMEDIATE_PROPAGATION_SYMBOL = ('__zone_symbol__propagationStopped');
37+
3638
export interface PatchEventTargetOptions {
3739
validateHandler?: (nativeDelegate: any, delegate: any, target: any, args: any) => boolean;
3840
addEventListenerFnName?: string;
@@ -105,6 +107,9 @@ export function patchEventTarget(
105107
// the callback will remove itself or other listener
106108
const copyTasks = tasks.slice();
107109
for (let i = 0; i < copyTasks.length; i++) {
110+
if (event && (event as any)[IMMEDIATE_PROPAGATION_SYMBOL] === true) {
111+
break;
112+
}
108113
invokeTask(copyTasks[i], target, event);
109114
}
110115
}
@@ -127,6 +132,9 @@ export function patchEventTarget(
127132
// the callback will remove itself or other listener
128133
const copyTasks = tasks.slice();
129134
for (let i = 0; i < copyTasks.length; i++) {
135+
if (event && (event as any)[IMMEDIATE_PROPAGATION_SYMBOL] === true) {
136+
break;
137+
}
130138
invokeTask(copyTasks[i], target, event);
131139
}
132140
}
@@ -564,3 +572,14 @@ export function findEventTasks(target: any, eventName: string): Task[] {
564572
}
565573
return foundTasks;
566574
}
575+
576+
export function patchEventPrototype(global: any, api: _ZonePrivate) {
577+
const Event = global['Event'];
578+
if (Event && Event.prototype) {
579+
api.patchMethod(
580+
Event.prototype, 'stopImmediatePropagation',
581+
(delegate: Function) => function(self: any, args: any[]) {
582+
self[IMMEDIATE_PROPAGATION_SYMBOL] = true;
583+
});
584+
}
585+
}

test/browser/browser.spec.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -870,6 +870,42 @@ describe('Zone', function() {
870870
button.removeEventListener('click', listener);
871871
}));
872872

873+
it('should support Event.stopImmediatePropagation',
874+
ifEnvSupports(supportEventListenerOptions, function() {
875+
const hookSpy = jasmine.createSpy('hook');
876+
const logs: string[] = [];
877+
const zone = rootZone.fork({
878+
name: 'spy',
879+
onScheduleTask: (parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone,
880+
task: Task): any => {
881+
hookSpy();
882+
return parentZoneDelegate.scheduleTask(targetZone, task);
883+
}
884+
});
885+
886+
const listener1 = (e: Event) => {
887+
logs.push('listener1');
888+
e.stopImmediatePropagation();
889+
};
890+
891+
const listener2 = (e: Event) => {
892+
logs.push('listener2');
893+
};
894+
895+
zone.run(function() {
896+
(button as any).addEventListener('click', listener1);
897+
(button as any).addEventListener('click', listener2);
898+
});
899+
900+
button.dispatchEvent(clickEvent);
901+
902+
expect(hookSpy).toHaveBeenCalled();
903+
expect(logs).toEqual(['listener1']);
904+
905+
button.removeEventListener('click', listener1);
906+
button.removeEventListener('click', listener2);
907+
}));
908+
873909
it('should support remove event listener by call zone.cancelTask directly', function() {
874910
let logs: string[] = [];
875911
let eventTask: Task;

0 commit comments

Comments
 (0)