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

fix(event): should handle event.stopImmediatePropagration #903

Merged
merged 1 commit into from
Sep 15, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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