In our code base we have a virtual repeat directive that uses Renderer2 to create a div like this:
this.renderer2.createElement('div');
In the ngOnDestroy method we are destroying it like this:
this.renderer.destroyNode(this.offsetBeforeEl);
This worked fine and we had no issues until we built the application in prod mode and we started getting the following error:
main.js?d73b003…:formatted:87193 Uncaught (in promise) TypeError: this.renderer.destroyNode is not a function
I added a breakpoint to the that line and found that in fact destroyNode is not a method on Renderer2. I went to the source code for angular and found the comment above the method in the abstract class defintion:
/**
* This property is allowed to be null / undefined,
* in which case the view engine won't call it.
* This is used as a performance optimization for production mode.
*/
destroyNode: ((node: any) => void)|null;
So I checked out the code for the view and saw this:
if (view.renderer.destroyNode) {
destroyViewNodes(view);
}
if (isComponentView(view)) {
view.renderer.destroy();
}
If I can't rely on this method existing, what is the correct way to destroy a node that was dynamically created using Renderer2?
Using Renderer2 Use ElementRef & ViewChild to get the reference to the DOM element, which you want to manipulate. @ViewChild('hello', { static: false }) divHello: ElementRef; Use the methods like setProperty , setStyle etc to change the property, styles of the element as shown below.
The Renderer class has been marked as deprecated since Angular version 4. This section provides guidance on migrating from this deprecated API to the newer Renderer2 API and what it means for your app.
Using the Renderer for manipulating the DOM doesn't break server-side rendering or Web Workers (where direct access to the DOM would break). ElementRef is a class that can hold a reference to a DOM element. This is again an abstraction to not break in environments where the browsers DOM isn't actually available.
The Renderer2 class is an abstraction provided by Angular in the form of a service that allows to manipulate elements of your app without having to touch the DOM directly.
To remove all children you can must iterate over the child elements.
Note that children
isn't a true array so it must be converted first.
Array.from(this.elementRef.nativeElement.children).forEach(child => {
console.log('children.length=' + this.elementRef.nativeElement.children.length);
this.renderer.removeChild(this.elementRef.nativeElement, child);
});
The logging statement demonstrates that the children aren't actually removed in 'real time' from the DOM - which is why a while loop doesn't work to remove lastChild
.
Use caution if these elements weren't originally added by you - because if they're components you may get memory leaks or all kinds of weirdness. The above was only tested for elements I'd added myself to an empty element node.
I thought I should be able to do [...this.elementRef.nativeElement.children]
, but this complained that slice
doesn't exist - so I just reverted to Array.from
.
Use renderer.removeChild
method.
until we built the application in prod mode
destroyNode
method is defined on the DebugRenderer2 which is not used in the production mode.
The correct way to handle node removal can be inferred from the way angular does it, for example this execRenderNodeAction function:
function execRenderNodeAction(
view: ViewData, renderNode: any, action: RenderNodeAction, parentNode: any, nextSibling: any,
target?: any[]) {
const renderer = view.renderer;
switch (action) {
case RenderNodeAction.RemoveChild:
renderer.removeChild(parentNode, renderNode);
break;
So, use removeChild
like this:
const parent = this.renderer.createElement('div');
const child = this.renderer.createElement('span');
this.renderer.appendChild(parent, child);
// using remove child
this.renderer.removeChild(parent, child);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With