diff --git a/packages/core/src/view/services.ts b/packages/core/src/view/services.ts index 8c705f378e80e..1ec512853b383 100644 --- a/packages/core/src/view/services.ts +++ b/packages/core/src/view/services.ts @@ -688,7 +688,11 @@ export class DebugRenderer2 implements Renderer2 { constructor(private delegate: Renderer2) { this.data = this.delegate.data; } destroyNode(node: any) { - removeDebugNodeFromIndex(getDebugNode(node) !); + const debugNode = getDebugNode(node) !; + removeDebugNodeFromIndex(debugNode); + if (debugNode instanceof DebugNode__PRE_R3__) { + debugNode.listeners.length = 0; + } if (this.delegate.destroyNode) { this.delegate.destroyNode(node); } diff --git a/packages/core/test/debug/debug_node_spec.ts b/packages/core/test/debug/debug_node_spec.ts index c8d9e2ef12ad5..c01c8f1351a90 100644 --- a/packages/core/test/debug/debug_node_spec.ts +++ b/packages/core/test/debug/debug_node_spec.ts @@ -8,7 +8,7 @@ import {CommonModule, NgIfContext} from '@angular/common'; -import {Component, DebugNode, Directive, ElementRef, EmbeddedViewRef, EventEmitter, HostBinding, Injectable, Input, NO_ERRORS_SCHEMA, Renderer2, TemplateRef, ViewChild, ViewContainerRef} from '@angular/core'; +import {Component, DebugNode, Directive, ElementRef, EmbeddedViewRef, EventEmitter, HostBinding, Injectable, Input, NO_ERRORS_SCHEMA, Output, Renderer2, TemplateRef, ViewChild, ViewContainerRef} from '@angular/core'; import {ComponentFixture, TestBed, async} from '@angular/core/testing'; import {By} from '@angular/platform-browser/src/dom/debug/by'; import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; @@ -927,5 +927,40 @@ class TestCmptWithPropBindings { expect(div.attributes.foo).toBe('bar'); }); + it('should clear event listeners when node is destroyed', () => { + let calls = 0; + @Component({ + selector: 'cancel-button', + template: '', + }) + class CancelButton { + @Output() cancel = new EventEmitter(); + } + + @Component({ + template: '', + }) + class App { + visible = true; + cancel() { calls++; } + } + + TestBed.configureTestingModule({declarations: [App, CancelButton]}); + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + + const button = fixture.debugElement.query(By.directive(CancelButton)); + button.triggerEventHandler('cancel', {}); + + expect(calls).toBe(1, 'Expected calls to be 1 after one event.'); + + fixture.componentInstance.visible = false; + fixture.detectChanges(); + + button.triggerEventHandler('cancel', {}); + + expect(calls).toBe(1, 'Expected calls to stay 1 after destroying the node.'); + }); + }); }