Renderer2 In Angular
The Renderer2 allows us to manipulate DOM elements without having to directly access the DOM. It creates a barrier between the DOM element and the code of the component. We can construct an element with Renderer2, add a text node to it, append a child element with the append child method, and so on. We can also change the styles, HTML attributes, CSS classes and properties, and so on. We can also attach events and listen to them.
Why not ElementRef?
To manipulate the DOM, we can use ElelemtRef's nativeElement attribute. This is what we learned in our last ElementRef article. The underlying DOM object is referenced by the nativeElement Property. This bypasses Angular and allows us direct access to the DOM. Although there is no harm in taking it, it is not recommended for the following reasons.
- Angular uses Templates, data binding, and change detection to keep the Component and the view in sync. When we update the DOM directly, we skip all of them.
- Only the browser supports DOM manipulation. You will not be able to use the App on other platforms where there is no browser, such as a web worker, a server (Server-side rendering), a desktop, or a mobile app.
- The data is not sanitized by the DOM APIs. As a result, we can inject a script, making our app an easy target for an XSS injection attack.
Using Renderer2
To begin, import it from the @angular/core folder.
import {Component, Renderer2, ElementRef, ViewChild, AfterViewInit } from '@angular/core';
injecting it into the part
constructor(private renderer:Renderer2) { }
To gain a reference to the DOM element you want to change, use ElementRef and ViewChild.
@ViewChild('hello', { static: false }) divHello: ElementRef;
To update the property and styles of the element, use methods like setProperty, setStyle, and so on, as illustrated below.
this.renderer.setProperty(this.divHello.nativeElement,'innerHTML',"Hello Angular") this.renderer.setStyle(this.divHello.nativeElement, 'color', 'red');
Setting & Removing Styles (setStyle & removeStyle)
abstract setStyle(el: any, style: string, value: any, flags?: RendererStyleFlags2): void abstract removeStyle(el: any, style: string, flags?: RendererStyleFlags2): void
Example
//Template <div #hello>Hello !</div>
//Component @ViewChild('hello', { static: false }) divHello: ElementRef; setStyle() { this.renderer.setStyle(this.divHello.nativeElement, 'color', 'blue'); } removeStyle() { this.renderer.removeStyle(this.divHello.nativeElement, 'color'); }
Adding / Removing CSS Classes (addClass & removeClass)
abstract addClass(el: any, name: string): void
abstract removeClass(el: any, name: string): void
Example
//Template <div #hello>Hello !</div>
//Component @ViewChild('hello', { static: false }) divHello: ElementRef; addClass() { this.renderer.addClass(this.divHello.nativeElement, 'blackborder' ); } removeClass() { this.renderer.removeClass(this.divHello.nativeElement, 'blackborder'); }
Setting or Remove Attributes (setAttribute & removeAttribute)
setAttribute(el: any, name: string, value: string, namespace?: string): void removeAttribute(el: any, name: string, namespace?: string): void
Example
//Template <h2>Add/ Remove Attributes </h2> <input #inputElement type='text'> <button (click)="addAttribute()">Set Attribute</button> <button (click)="removeAttribute()">Remove Attribute</button>
//Component @ViewChild('inputElement', { static: false }) inputElement: ElementRef; addAttribute() { this.renderer.setAttribute(this.inputElement.nativeElement, 'value', 'name' ); } removeAttribute() { this.renderer.removeAttribute(this.inputElement.nativeElement, 'value'); }
Setting Property (setProperty)
setProperty(el: any, name: string, value: any): void setProperty() { this.renderer.setProperty(this.divHello.nativeElement,'innerHTML',"Hello Angular") }
AppendChild
appendChild(parent: any, newChild: any): void
Insert Text Element (CreateText & appendChild)
//Template <h2>Create Text Example</h2> <div #divCreateText> </div> <button (click)="createText()">Create Text</button>
Inject the reference to the divCreateText in the component using the ViewChild.
@ViewChild('divCreateText', { static: false }) divCreateText: ElementRef;
To make a text node, use the createText method. It is not included in the DOM at this time.
const text = this.renderer.createText('Example of Create Text');
To add it to an existing element, use the appendChild method (divCreateText).
this.renderer.appendChild(this.divCreateText.nativeElement, text);
Creating new Element (createElement & appendChild)
constructor(private el: ElementRef, private renderer:Renderer2) { }
Using the method createElement('div'), create a new div element. It hasn't been added to the DOM yet.
const div = this.renderer.createElement('div');
To make a new text node, use the createText('Inserted at bottom') method.
const text = this.renderer.createText('Inserted at bottom');
To append the freshly produced text node to the div element, use appendChild. It's worth noting that div hasn't yet been added to the DOM.
this.renderer.appendChild(div, text);
Finally, the div element is added to an existing DOM element, the host element.
this.renderer.appendChild(this.div.nativeElement, div);
The whole code can be found below. The createElement2 function adds a new div to the new child node.
import { Component, Renderer2, OnInit, ElementRef, ViewChild, AfterViewInit, VERSION } from '@angular/core'; @Component({ selector: 'app-create-element', templateUrl: './create-element.component.html', styleUrls: ['./create-element.component.css'] }) export class CreateElementComponent { @ViewChild('div', { static: false }) div: ElementRef; constructor(private el: ElementRef, private renderer:Renderer2) { } createElement() { const div = this.renderer.createElement('div'); const text = this.renderer.createText('Inserted at bottom'); this.renderer.appendChild(div, text); this.renderer.appendChild(this.el.nativeElement, div); } createElement2() { const div = this.renderer.createElement('div'); const text = this.renderer.createText('Inserted inside div'); this.renderer.appendChild(div, text); this.renderer.appendChild(this.div.nativeElement, div); } }
Add the following code under the createElement.component.html:
<h2>Renderer2 Create Element</h2> <div #div style="border: 1px solid black;"> This is a div </div> <button (click)="createElement()">Create Element</button> <button (click)="createElement2()">Create Element</button>
InsertBefore
insertBefore(parent: any, newChild: any, refChild: any): void
<h1>Angular Renderer2 InsertBefore Example</h1> <div #div1> This is div 1 </div> <div #div2> This is div 2 <div #div3> This is div 3 </div> </div> <button (click)="insertBeforeDiv1()" >Insert Before Div1</button> <button (click)="insertBeforeDiv2()" >Insert Before Div2</button> <button (click)="insertBeforeDiv3()" >Insert Before Div3</button>
import { Component, OnInit, ViewChild, ElementRef, Renderer2 } from '@angular/core'; @Component({ selector: 'app-insert-before', templateUrl: './insert-before.component.html', styleUrls: ['./insert-before.component.css'] }) export class InsertBeforeComponent { @ViewChild('div1', { static: false }) div1: ElementRef; @ViewChild('div2', { static: false }) div2: ElementRef; @ViewChild('div3', { static: false }) div3: ElementRef; constructor(private renderer:Renderer2, private el:ElementRef) { } insertBeforeDiv1() { const div = this.renderer.createElement('div'); const text = this.renderer.createText('This Text is Inserted before the div1'); this.renderer.appendChild(div, text); this.renderer.insertBefore(this.el.nativeElement,div,this.div1.nativeElement); } insertBeforeDiv2() { const div = this.renderer.createElement('div'); const text = this.renderer.createText('This Text is Inserted before the div2'); this.renderer.appendChild(div, text); this.renderer.insertBefore(this.el.nativeElement,div,this.div2.nativeElement); } insertBeforeDiv3() { const div = this.renderer.createElement('div'); const text = this.renderer.createText('This Text is Inserted before the div3'); this.renderer.appendChild(div, text); //Using parentNode to retrieve the Parent Node this.renderer.insertBefore( this.renderer.parentNode(this.div3.nativeElement),div,this.div3.nativeElement); } }
Insert Comment
createComment(value: string): any
ParentNode & NextSibling
//Returns the parent Node of div3 this.renderer.parentNode(this.div3.nativeElement);
The nextSibling method in the host element's DOM returns the next sibling node of a given node.
//Returns the next Sibling node of div2 this.renderer.nextSibling(this.div2.nativeElement);
SelectRootElement
selectRootElement(selectorOrNode: any, preserveContent?: boolean)
<h1>Renderer2 selectRootElement Example</h1> <div class="outerDiv" style="border: 1px solid black; padding :5px;"> <div class="div1" style="border: 1px solid black; margin :5px;">This is Div1</div> <div class="div2" style="border: 1px solid black; margin :5px;">This is Div2</div> <div class="div3" class="div3class" style="border: 1px solid black; margin :5px;">This is Div3</div> </div>
exampleDiv1() { const e = this.renderer.selectRootElement('.div1',false); }
Examples
exampleDiv2() { //Conent is always replaced. becuase preserveContent is false const e = this.renderer.selectRootElement('.div2',false); const t = this.renderer.createText('Content added to div2'); this.renderer.appendChild(e, t); } exampleDiv3() { //Conent is always appended. becuase preserveContent is true const e = this.renderer.selectRootElement('.div3',true); const t = this.renderer.createText('Content added to div3'); this.renderer.appendChild(e, t); }
Listen to DOM events
abstract listen(target: any, eventName: string, callback: (event: any) => boolean | void): () => void
In the example below, we listen for a button's click event.
//Component import { Component, OnInit, ViewChild, ElementRef, Renderer2, AfterViewInit } from '@angular/core'; @Component({ selector: 'app-listen-events', templateUrl: './listen-events.component.html', styleUrls: ['./listen-events.component.css'] }) export class ListenEventsComponent implements AfterViewInit { @ViewChild('hello', { static: false }) divHello: ElementRef; Count=0 clicklistener; constructor(private renderer:Renderer2) { } ngAfterViewInit() { this.clicklistener = this.renderer.listen(this.divHello.nativeElement, 'click', (evt) => { this.Count++; }); } ngOnDestroy() { this.clicklistener.unsubscribe() } }
Please remember to unsubscribe from this clicklistener.unsubscribe().
//Template <h1>Renderer2 Listen Events Example</h1> <button #hello>hello</button> Click Count {{Count}}