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

Commit

Permalink
fix(event): should handle event.stopImmediatePropagration (#903)
Browse files Browse the repository at this point in the history
  • Loading branch information
JiaLiPassion authored and mhevery committed Sep 15, 2017
1 parent 956c729 commit dcc285a
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 7 deletions.
13 changes: 7 additions & 6 deletions lib/browser/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,15 @@ import {patchTimer} from '../common/timers';
import {patchClass, patchMacroTask, patchMethod, patchOnProperties, patchPrototype, zoneSymbol} from '../common/utils';

import {propertyPatch} from './define-property';
import {eventTargetPatch} from './event-target';
import {eventTargetPatch, patchEvent} from './event-target';
import {propertyDescriptorPatch} from './property-descriptor';
import {registerElementPatch} from './register-element';

Zone.__load_patch('util', (global: any, Zone: ZoneType, api: _ZonePrivate) => {
api.patchOnProperties = patchOnProperties;
api.patchMethod = patchMethod;
});

Zone.__load_patch('timers', (global: any, Zone: ZoneType, api: _ZonePrivate) => {
const set = 'set';
const clear = 'clear';
Expand Down Expand Up @@ -46,6 +51,7 @@ Zone.__load_patch('blocking', (global: any, Zone: ZoneType, api: _ZonePrivate) =
});

Zone.__load_patch('EventTarget', (global: any, Zone: ZoneType, api: _ZonePrivate) => {
patchEvent(global, api);
eventTargetPatch(global, api);
// patch XMLHttpRequestEventTarget's addEventListener/removeEventListener
const XMLHttpRequestEventTarget = (global as any)['XMLHttpRequestEventTarget'];
Expand Down Expand Up @@ -240,8 +246,3 @@ Zone.__load_patch('PromiseRejectionEvent', (global: any, Zone: ZoneType, api: _Z
findPromiseRejectionHandler('rejectionhandled');
}
});

Zone.__load_patch('util', (global: any, Zone: ZoneType, api: _ZonePrivate) => {
api.patchOnProperties = patchOnProperties;
api.patchMethod = patchMethod;
});
6 changes: 5 additions & 1 deletion lib/browser/event-target.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/

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

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

return true;
}

export function patchEvent(global: any, api: _ZonePrivate) {
patchEventPrototype(global, api);
}
19 changes: 19 additions & 0 deletions lib/common/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ export const ZONE_SYMBOL_PREFIX = '__zone_symbol__';

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

const IMMEDIATE_PROPAGATION_SYMBOL = ('__zone_symbol__propagationStopped');

export interface PatchEventTargetOptions {
validateHandler?: (nativeDelegate: any, delegate: any, target: any, args: any) => boolean;
addEventListenerFnName?: string;
Expand Down Expand Up @@ -105,6 +107,9 @@ export function patchEventTarget(
// the callback will remove itself or other listener
const copyTasks = tasks.slice();
for (let i = 0; i < copyTasks.length; i++) {
if (event && (event as any)[IMMEDIATE_PROPAGATION_SYMBOL] === true) {
break;
}
invokeTask(copyTasks[i], target, event);
}
}
Expand All @@ -127,6 +132,9 @@ export function patchEventTarget(
// the callback will remove itself or other listener
const copyTasks = tasks.slice();
for (let i = 0; i < copyTasks.length; i++) {
if (event && (event as any)[IMMEDIATE_PROPAGATION_SYMBOL] === true) {
break;
}
invokeTask(copyTasks[i], target, event);
}
}
Expand Down Expand Up @@ -564,3 +572,14 @@ export function findEventTasks(target: any, eventName: string): Task[] {
}
return foundTasks;
}

export function patchEventPrototype(global: any, api: _ZonePrivate) {
const Event = global['Event'];
if (Event && Event.prototype) {
api.patchMethod(
Event.prototype, 'stopImmediatePropagation',
(delegate: Function) => function(self: any, args: any[]) {
self[IMMEDIATE_PROPAGATION_SYMBOL] = true;
});
}
}
36 changes: 36 additions & 0 deletions test/browser/browser.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -870,6 +870,42 @@ describe('Zone', function() {
button.removeEventListener('click', listener);
}));

it('should support Event.stopImmediatePropagation',
ifEnvSupports(supportEventListenerOptions, function() {
const hookSpy = jasmine.createSpy('hook');
const logs: string[] = [];
const zone = rootZone.fork({
name: 'spy',
onScheduleTask: (parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone,
task: Task): any => {
hookSpy();
return parentZoneDelegate.scheduleTask(targetZone, task);
}
});

const listener1 = (e: Event) => {
logs.push('listener1');
e.stopImmediatePropagation();
};

const listener2 = (e: Event) => {
logs.push('listener2');
};

zone.run(function() {
(button as any).addEventListener('click', listener1);
(button as any).addEventListener('click', listener2);
});

button.dispatchEvent(clickEvent);

expect(hookSpy).toHaveBeenCalled();
expect(logs).toEqual(['listener1']);

button.removeEventListener('click', listener1);
button.removeEventListener('click', listener2);
}));

it('should support remove event listener by call zone.cancelTask directly', function() {
let logs: string[] = [];
let eventTask: Task;
Expand Down

0 comments on commit dcc285a

Please sign in to comment.