Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(core): should support event.stopImmediatePropagation #19222

Merged
merged 1 commit into from Nov 3, 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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
29 changes: 28 additions & 1 deletion packages/platform-browser/src/dom/events/dom_events.ts
Expand Up @@ -31,6 +31,8 @@ const FALSE = 'FALSE';
const ANGULAR = 'ANGULAR';
const NATIVE_ADD_LISTENER = 'addEventListener';
const NATIVE_REMOVE_LISTENER = 'removeEventListener';
// use the same symbol string which is used in zone.js
const stopSymbol = '__zone_symbol__propagationStopped';

const blackListedEvents: string[] = Zone && Zone[__symbol__('BLACK_LISTED_EVENTS')];
let blackListedMap: {[eventName: string]: string};
Expand Down Expand Up @@ -77,6 +79,9 @@ const globalListener = function(event: Event) {
// itself or others
const copiedTasks = taskDatas.slice();
for (let i = 0; i < copiedTasks.length; i++) {
if ((event as any)[stopSymbol] === true) {
break;
}
const taskData = copiedTasks[i];
if (taskData.zone !== Zone.current) {
// only use Zone.run when Zone.current not equals to stored zone
Expand All @@ -90,7 +95,29 @@ const globalListener = function(event: Event) {

@Injectable()
export class DomEventsPlugin extends EventManagerPlugin {
constructor(@Inject(DOCUMENT) doc: any, private ngZone: NgZone) { super(doc); }
constructor(@Inject(DOCUMENT) doc: any, private ngZone: NgZone) {
super(doc);

this.patchEvent();
}

private patchEvent() {
if (!Event || !Event.prototype) {
return;
}
const symbol = '__zone_symbol__stopImmediatePropagation';
if ((Event.prototype as any)[symbol]) {
// already patched by zone.js
return;
}
(Event.prototype as any)[symbol] = Event.prototype.stopImmediatePropagation;
Event.prototype.stopImmediatePropagation = function() {
if (this) {
this[stopSymbol] = true;
}
};
}


// This plugin should come last in the list of plugins, because it accepts all
// events.
Expand Down
36 changes: 36 additions & 0 deletions packages/platform-browser/test/dom/events/event_manager_spec.ts
Expand Up @@ -152,6 +152,42 @@ export function main() {
expect(receivedEvents).toEqual([]);
});

it('should support event.stopImmediatePropagation', () => {
const Zone = (window as any)['Zone'];

const element = el('<div><div></div></div>');
getDOM().appendChild(doc.body, element);
const dispatchedEvent = getDOM().createMouseEvent('click');
let receivedEvents: any[] /** TODO #9100 */ = [];
let receivedZones: any[] = [];
const handler1 = (e: any /** TODO #9100 */) => {
receivedEvents.push(e);
receivedZones.push(Zone.current.name);
e.stopImmediatePropagation();
};
const handler2 = (e: any /** TODO #9100 */) => {
receivedEvents.push(e);
receivedZones.push(Zone.current.name);
};
const manager = new EventManager([domEventPlugin], new FakeNgZone());

let remover1 = null;
let remover2 = null;
Zone.root.run(() => { remover1 = manager.addEventListener(element, 'click', handler1); });
Zone.root.fork({name: 'test'}).run(() => {
remover2 = manager.addEventListener(element, 'click', handler2);
});
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvents).toEqual([dispatchedEvent]);
expect(receivedZones).toEqual([Zone.root.name]);

receivedEvents = [];
remover1 && remover1();
remover2 && remover2();
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvents).toEqual([]);
});

it('should handle event correctly when one handler remove itself ', () => {
const Zone = (window as any)['Zone'];

Expand Down