fix(core): error if DebugRenderer2.destroyNode is called twice in a row (#41565)

Fixes an error that will be thrown if  `DebugRenderer2.destroyNode` is called with a node that has already been destroyed. The error happened, because we had a non-null assertion, even though the value can be null.

Note that this fix applies only to ViewEngine, because Ivy doesn't provide the `DebugRenderer2`. I decided to resolve it, because it fix is straightforward and this error has been showing up in our logs for a long time now, making actual errors harder to find.

PR Close #41565
This commit is contained in:
Kristiyan Kostadinov 2021-04-11 11:15:12 +02:00 committed by Zach Arend
parent ebc80b3b5c
commit aa0e54fe97
2 changed files with 25 additions and 4 deletions

View File

@ -703,11 +703,15 @@ export class DebugRenderer2 implements Renderer2 {
}
destroyNode(node: any) {
const debugNode = getDebugNode(node)!;
removeDebugNodeFromIndex(debugNode);
if (debugNode instanceof DebugNode__PRE_R3__) {
debugNode.listeners.length = 0;
const debugNode = getDebugNode(node);
if (debugNode) {
removeDebugNodeFromIndex(debugNode);
if (debugNode instanceof DebugNode__PRE_R3__) {
debugNode.listeners.length = 0;
}
}
if (this.delegate.destroyNode) {
this.delegate.destroyNode(node);
}

View File

@ -208,6 +208,7 @@ class TestApp {
width = 200;
color = 'red';
isClosed = true;
constructor(public renderer: Renderer2) {}
}
@Component({selector: 'test-cmpt', template: ``})
@ -618,6 +619,22 @@ class TestCmptWithPropInterpolation {
expect(fixture.debugElement.query(By.css('.myclass'))).toBeTruthy();
});
it('should not throw when calling DebugRenderer2.destroyNode twice in a row', () => {
const fixture = TestBed.createComponent(TestApp);
fixture.detectChanges();
const firstChild = fixture.debugElement.children[0];
const renderer = fixture.componentInstance.renderer;
expect(firstChild).toBeTruthy();
expect(() => {
// `destroyNode` needs to be null checked, because only ViewEngine provides a
// `DebugRenderer2` which has the behavior we're testing for. Ivy provides
// `BaseAnimationRenderer` which doesn't have the issue.
renderer.destroyNode?.(firstChild);
renderer.destroyNode?.(firstChild);
}).not.toThrow();
});
describe('DebugElement.query with dynamically created descendant elements', () => {
let fixture: ComponentFixture<{}>;
beforeEach(() => {