Correct way to do DOM Manipulation in Angular 2+
Any change to a document's aesthetics like page navigation (routing)
, item selection (ngIf)
, loop iterations(ngFor)
etc. is DOM Manipulation
. It can be either property
driven or event
triggered or reference
handled.
Angular offers multiple ways to handle DOM Manipulation
.
EVENT BINDING
:The flow of information from
elements
in a component to the corresponding component'sclass
is event binding(HTML Template to TS)
. Event binding works without having to define atemplate reference variable.
This is the best and the easiest method to manipulate DOM elements.()
denotesevent binding
.Below is the snippet for our example:
HTML
<button (click)="changeColour()" [ngStyle]="{'background-color': buttonColor}">BUY NOW</button>
TS
buttonColor : string = 'grey' changeColour() { this.buttonColor = 'purple' }
Angular also has a feature to allow event listener to be implemented only on a
particular event
, e.g whenenter key is pressed
,mouse clicked
or acombination of keys is pressed
.Below is the snippet for our example:
HTML
<button (keyup.control.shift.enter)="changeColour()" [ngStyle]="{'background-color': buttonColor}">BUY NOW</button>
The
colour
of the button becomespurple
whenCtrl+Shift+Enter
is pressed.@HostListener and @HostBinding
:This is similar to
event binding
andproperty binding
in angular.@HostBinding('value') val;
is same as[value]="val"
and
@HostListener('click') click(){ }
is same as(click)="click()"
.@HostBinding
and@HostListener
are defined insidedirective
whereas[]
and()
are defined inside thecomponent template
.Below is the snippet for our example:
HTML
<button class="c_highlight">BUY NOW</button>
TS
(host.directive.ts)
@Directive({ // Notice the . in selector => this directive will work for DOM with the c_highlight class selector: '.c_highlight' }) export class HostDirective { @HostBinding('style.backgroundColor') c_color = "red"; @HostListener('click') c_onclick() { this.c_color = "purple" ; } }
Renderer2
:This is basically a
wrapper
over thebrowser API for DOM Manipulation
. The Renderer2 API can be runacross platforms
other than the DOM and you can provide yourown Renderer2 implementation
, unique to a platform. There are multiple DOM manipulationmethods
present for the same likesetStyle()
,createElement()
,createText()
,appendChild()
etc. and we can implement our owncustom
methods too. This is similar to thetemplate reference variable
in your example and we are using thereference
to the element tomodify
itsproperties
.Below is the snippet for our example:
HTML
<button (click) = "onClick()" #abcd>BUY NOW</button>
TS
@ViewChild('abcd') private abcd: ElementRef; constructor(private renderer: Renderer2) { } onClick() { this.renderer.setStyle(this.abcd.nativeElement, 'backgroundColor','purple'); }
Read More - https://angular.io/api/core/Renderer2
Template Reference Variable
:This involves creating an
id (reference)
for the element. This is similar to thejquery
approach wherein each element can have anid
and events can be defined on these elements by using thegetElementById()
method. Example (as shown in your question):HTML
<button (click)="changeColour()" id="buy-now">BUY NOW</button>
TS
changeColour() { const b = <HTMLElement>document.querySelector('#buy-now'); b.style.backgroundColour = 'purple' }
fromEvent()
fromrxjs
: This is similar to defining anEvent Listener
on an element. ThefromEvent
method creates anObservable
that emits events of a specific type coming from the given element. Only thereference
for the element has to be declared; the event isassociated
with this reference. Example:HTML
<button #abcd>BUY NOW</button>
TS
@ViewChild('abcd') private abcd: ElementRef; ngOnInit(){ fromEvent(this.abcd.nativeElement, 'click').subscribe(res => this.abcd.nativeElement.style.backgroundColor = 'purple'); }
SUMMARY:
The choice for the technique used for
DOM Manipulation
depends solely on thedeveloper
. Each of these methods have their ownbenefits
andtrade-offs
; like for Event Binding,performance
can be relatively slower when a large list is being modified as thechange detection cycle
can only run again once this function returns. Method 1 and 2 are thebest angular practices
as theseavoid creating references
for elements which can be risky and can make your application morevulnerable
toXSS
attacks as pointed out by @Chellapan.
According to Angular Documentation Using Element Ref is Vulnerable
Permitting direct access to the DOM can make your application more vulnerable to XSS attacks. Carefully review any use of ElementRef in your code. For more detail, see the Security Guide.
Use Renderer2 to manipulate the DOM.
Create a Template Ref in template and pass it to the changeColour method and Use renderer2 service which provide setStyle method to set the style of the element
component.html
<button #button (click)="changeColour(button)">BUY NOW</button>
component.ts
constructor(private renderer: Renderer2) { }
changeColour(element: HTMLElement) {
this.renderer.setStyle(element.nativeElement, 'backgroundColour ', 'purple');
}
Ref:https://angular.io/api/core/ElementRef#security-risk