Skip to content

Commit a740e4f

Browse files
JiaLiPassionmhevery
authored andcommitted
fix(core): fix angular#20532, should be able to cancel listener from mixed zone (angular#20538)
PR Close angular#20538
1 parent 2a9d2ba commit a740e4f

2 files changed

Lines changed: 53 additions & 3 deletions

File tree

packages/platform-browser/src/dom/events/dom_events.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -203,16 +203,26 @@ export class DomEventsPlugin extends EventManagerPlugin {
203203
// just call native removeEventListener
204204
return target[NATIVE_REMOVE_LISTENER].apply(target, [eventName, callback, false]);
205205
}
206+
// fix issue 20532, should be able to remove
207+
// listener which was added inside of ngZone
208+
let found = false;
206209
for (let i = 0; i < taskDatas.length; i++) {
207210
// remove listener from taskDatas if the callback equals
208211
if (taskDatas[i].handler === callback) {
212+
found = true;
209213
taskDatas.splice(i, 1);
210214
break;
211215
}
212216
}
213-
if (taskDatas.length === 0) {
214-
// all listeners are removed, we can remove the globalListener from target
215-
underlyingRemove.apply(target, [eventName, globalListener, false]);
217+
if (found) {
218+
if (taskDatas.length === 0) {
219+
// all listeners are removed, we can remove the globalListener from target
220+
underlyingRemove.apply(target, [eventName, globalListener, false]);
221+
}
222+
} else {
223+
// not found in taskDatas, the callback may be added inside of ngZone
224+
// use native remove listener to remove the calback
225+
target[NATIVE_REMOVE_LISTENER].apply(target, [eventName, callback, false]);
216226
}
217227
}
218228
}

packages/platform-browser/test/dom/events/event_manager_spec.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,46 @@ export function main() {
255255
expect(receivedEvents).toEqual([]);
256256
});
257257

258+
it('should be able to remove event listener which was added inside of ngZone', () => {
259+
const Zone = (window as any)['Zone'];
260+
261+
const element = el('<div><div></div></div>');
262+
getDOM().appendChild(doc.body, element);
263+
const dispatchedEvent = getDOM().createMouseEvent('click');
264+
let receivedEvents: any[] /** TODO #9100 */ = [];
265+
let receivedZones: any[] = [];
266+
const handler1 = (e: any /** TODO #9100 */) => {
267+
receivedEvents.push(e);
268+
receivedZones.push(Zone.current.name);
269+
};
270+
const handler2 = (e: any /** TODO #9100 */) => {
271+
receivedEvents.push(e);
272+
receivedZones.push(Zone.current.name);
273+
};
274+
const manager = new EventManager([domEventPlugin], new FakeNgZone());
275+
276+
let remover1 = null;
277+
let remover2 = null;
278+
// handler1 is added in root zone
279+
Zone.root.run(() => { remover1 = manager.addEventListener(element, 'click', handler1); });
280+
// handler2 is added in 'angular' zone
281+
Zone.root.fork({name: 'fakeAngularZone', properties: {isAngularZone: true}}).run(() => {
282+
remover2 = manager.addEventListener(element, 'click', handler2);
283+
});
284+
getDOM().dispatchEvent(element, dispatchedEvent);
285+
expect(receivedEvents).toEqual([dispatchedEvent, dispatchedEvent]);
286+
expect(receivedZones).toEqual([Zone.root.name, 'fakeAngularZone']);
287+
288+
receivedEvents = [];
289+
remover1 && remover1();
290+
remover2 && remover2();
291+
getDOM().dispatchEvent(element, dispatchedEvent);
292+
// handler1 and handler2 are added in different zone
293+
// one is angular zone, the other is not
294+
// should still be able to remove them correctly
295+
expect(receivedEvents).toEqual([]);
296+
});
297+
258298
it('should run blackListedEvents handler outside of ngZone', () => {
259299
const Zone = (window as any)['Zone'];
260300
const element = el('<div><div></div></div>');

0 commit comments

Comments
 (0)