fix(core): DebugElement.listeners not cleared on destroy (#31820)
Currently the `DebugElement.listeners` array are retained after the node is destroyed. This means that they'll continue to fire through `triggerEventHandler` and can cause memory leaks. This has already been fixed in Ivy, but these changes fix it in ViewEngine for consistency. PR Close #31820
This commit is contained in:
parent
5f0d5e9ccf
commit
14dba72aee
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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<void>();
|
||||
}
|
||||
|
||||
@Component({
|
||||
template: '<cancel-button *ngIf="visible" (cancel)="cancel()"></cancel-button>',
|
||||
})
|
||||
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.');
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue