A Guide to DOM Manipulation in Angular 14: Improved Performance

Vikas Tiwari
7 min readJun 10, 2023

DOM manipulation is a fundamental concept in Angular 14 that empowers you to interact with the Document Object Model (DOM) of your application.

In this blog post, we will explore simple techniques for manipulating the DOM in Angular 14, using a tutorial-oriented approach. Moreover, we will highlight the significance of performance and explore several techniques that can reduce re-rendering and improve overall performance.

Table of Contents:

  1. Understanding DOM Manipulation in Angular 14
  2. Direct DOM Manipulation with ElementRef
  3. Renderer2: Safe and Platform-Agnostic DOM Manipulation
  4. ViewChild and Template Reference Variables
  5. Dynamic Styling with NgStyle and NgClass Directives
  6. Event Binding and Dynamic Interactions
  7. Dynamic Component Creation and Manipulation
  8. Optimizing Performance with Change Detection Strategies
  9. Best Practices for DOM Manipulation in Angular 14

Understanding DOM Manipulation in Angular 14

  1. Introduction to the Document Object Model (DOM): The Document Object Model (DOM) is a programming interface that depicts the arrangement of an HTML or XML document. It offers a means to access, manipulate, and alter the components of a web page using JavaScript or other programming languages.
  2. When to use DOM manipulation in Angular: Although Angular encourages the utilization of declarative programming and data-binding to modify the DOM, certain circumstances call for direct DOM manipulation. Here are several scenarios in which you might contemplate utilizing DOM manipulation in Angular:
  • Integrating with third-party libraries: Certain libraries may necessitate direct DOM manipulation for initialization or interaction purposes. For instance, when employing a date picker library, you might need to manipulate the DOM to establish the initial date or manage user interactions.
  • Custom animations and visual effects: Even though Angular offers animation capabilities, there may arise situations where precise control over the DOM is required to create customized animations or visual effects. Directly manipulating the DOM can provide greater control over the elements involved in the animation, enabling you to achieve the desired effects.
  • Accessing or modifying specific DOM elements: There are certain scenarios where accessing or modifying specific DOM elements that are not directly managed by Angular becomes necessary. This might involve applying styles, altering attributes, or adding event listeners to elements that exist outside the scope of Angular components.
  • Performance optimizations: In certain instances, direct DOM manipulation can provide performance advantages compared to Angular’s data-binding. When dealing with substantial datasets or intricate UI interactions, selectively updating the DOM through efficient techniques can enhance overall performance.
<div #myDiv>
<p>Original Text</p>
</div>
<button (click)="changeText()">Change Text</button>
import { Component, ElementRef, ViewChild } from '@angular/core';

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
@ViewChild('myDiv') myDiv!: ElementRef;

changeText(): void {
const paragraph = this.myDiv.nativeElement.querySelector('p');
paragraph.textContent = 'Modified Text';
}
}

Direct DOM Manipulation with ElementRef

import { Component, ElementRef, ViewChild } from '@angular/core';

@Component({
selector: 'app-example',
template: `
<div #myElement>
<p>Hello, Angular!</p>
</div>
<button (click)="updateText()">Change Text</button>
`
})
export class ExampleComponent {
@ViewChild('myElement', { static: true }) myElementRef!: ElementRef;

updateText() {
const element = this.myElementRef.nativeElement;
const paragraph = element.querySelector('p');
paragraph.textContent = 'Update DOM!';
element.style.backgroundColor = 'yellow';
}
}

In the above example, we have a component called ExampleComponent. It contains an div element with an embedded p element and a button to trigger the text and style change.

To access the div element using ElementRef, we use the ViewChild decorator with the static: true option to ensure that the view child is available during component initialization. We assign it to the myElementRef property.

Inside the updateText method, we obtain the native DOM element from myElementRef.nativeElement. We then use standard DOM manipulation techniques to select the p element and modify its text content.

Renderer2: Safe and Platform-Agnostic DOM Manipulation

import { Component, Renderer2, ElementRef, OnInit } from '@angular/core';

@Component({
selector: 'app-dom-manipulation',
template: `
<button (click)="createDynamicElement()">Create Element</button>
<div #container></div>
`
})
export class DomManipulationComponent implements OnInit {
constructor(private renderer: Renderer2, private elementRef: ElementRef) {}

ngOnInit() {
const container = this.elementRef.nativeElement.querySelector('#container');
this.renderer.setStyle(container, 'border', '2px solid black');
}

createDynamicElement() {
const container = this.elementRef.nativeElement.querySelector('#container');
const dynamicElement = this.renderer.createElement('p');
const text = this.renderer.createText('Dynamic element created using Renderer2!');
this.renderer.appendChild(dynamicElement, text);
this.renderer.appendChild(container, dynamicElement);
}
}

In the ngOnInit() method, we access the #container element using ElementRef and apply a style using Renderer2. This demonstrates how to safely modify the style of an existing DOM element.

we use Renderer2 to create a new <p> element and add text content to it. We then append the dynamic element to the #container element, creating it dynamically.

ViewChild and Template Reference Variables

<!-- app.component.html -->
<h1 #headerEle>ViewChild and Template Reference Example</h1>
<button (click)="updateHeaderText()">Update Text</button>
// app.component.ts
import { Component, ViewChild, ElementRef } from '@angular/core';

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
@ViewChild('headerEle', { static: true }) headerRef: ElementRef;

updateHeaderText() {
this.headerRef.nativeElement.textContent = 'Updated Header Text';
}
}

We use @ViewChild decorator to get a reference the headerEle in our component class.

By leveraging ViewChild and template reference variables, we gain the ability to directly access and manipulate DOM elements from within our component class. This empowers us to dynamically modify the DOM in response to user interactions or other application logic.

Dynamic Styling with NgStyle and NgClass Directives

  1. Applying dynamic styles to DOM elements using NgStyle:
<div [ngStyle]="{'color': dynamicColor, 'font-size': dynamicSize}">
This text will have dynamic styles applied.
</div>
import { Component } from '@angular/core';

@Component({
selector: 'app-example',
templateUrl: './example.component.html',
styleUrls: ['./example.component.css']
})
export class ExampleComponent {
dynamicColor: string = 'red';
dynamicSize: string = '20px';
}

The ngStyle the directive is used to apply dynamic styles to the div element.

2. Conditionally applying CSS classes with NgClass:

<div [ngClass]="{'active': isActive, 'highlight': isHighlighted}">
This element will have conditional CSS classes applied.
</div>
import { Component } from '@angular/core';

@Component({
selector: 'app-example',
templateUrl: './example.component.html',
styleUrls: ['./example.component.css']
})
export class ExampleComponent {
isActive: boolean = true;
isHighlighted: boolean = false;
}

The ngClass the directive is used to conditionally apply CSS classes to the div element.

Event Binding and Dynamic Interactions

<button (click)="handleButtonClick()">Click Me</button>
<p>{{ message }}</p>
import { Component } from '@angular/core';

@Component({
selector: 'app-example',
templateUrl: './example.component.html',
styleUrls: ['./example.component.scss']
})
export class ExampleComponent {
message: string = '';

handleButtonClick() {
this.message = 'Button clicked!';
}
}

The button element with an event binding (click)="handleButtonClick()". This binds the click event of the button to the handleButtonClick() method in the component class.

Dynamic Component Creation and Manipulation

By utilizing dynamic component creation and manipulation, you can elevate the flexibility and adaptability of your Angular 14 application. This approach proves particularly valuable when there is a need to dynamically generate components based on user interactions or data.

<div #dynamicContainer></div>
<button (click)="createDynamicComponent()">Create Dynamic Component</button>
<button (click)="destroyDynamicComponent()">Destroy Dynamic Component</button>
@Component({
selector: 'app-parent',
template: `
<div #dynamicContainer></div>
<button (click)="createDynamicComponent()">Create Dynamic Component</button>
<button (click)="destroyDynamicComponent()">Destroy Dynamic Component</button>
`
})
export class ParentComponent {
@ViewChild('dynamicContainer', { read: ViewContainerRef }) dynamicContainer: ViewContainerRef;

constructor(private componentFactoryResolver: ComponentFactoryResolver) {}

createDynamicComponent() {
// Create a component factory for the DynamicComponent
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(DynamicComponent);

// Create an instance of the DynamicComponent
const componentRef = this.dynamicContainer.createComponent(componentFactory);

// Access the instance of the DynamicComponent
const dynamicComponentInstance = componentRef.instance;

// Set properties or pass data to the DynamicComponent
dynamicComponentInstance.title = 'Dynamic Component Title';
dynamicComponentInstance.description = 'Dynamic Component Description';
}

destroyDynamicComponent() {
this.dynamicContainer.clear();
}
}

Optimizing Performance with Change Detection Strategies

  1. Introduction to Change Detection Strategies in Angular:
  • Angular offers various change detection strategies that govern the timing and mechanism of updates in your components. By default, Angular employs the Default change detection strategy, which triggers change detection for all components whenever an event or asynchronous operation occurs.
  • The OnPush change detection strategy is an optimization technique aimed at reducing unnecessary re-rendering of components. With this strategy, Angular only performs update checks if there are changes in the component’s input properties or if an event is triggered within the component or its subtree. This approach helps improve performance by minimizing unnecessary rendering operations.

2. Using OnPush change detection to minimize re-rendering:

  • In the example component, we specify the change detection strategy as OnPush by setting changeDetection: ChangeDetectionStrategy.OnPush in the component's metadata.
  • This change detection strategy tells Angular to perform change detection only when the component’s input properties change or when explicitly triggered using markForCheck() method.

3. Applying ChangeDetectorRef for targeted change detection:

  • In the constructor, we inject the ChangeDetectorRef instance as cdr.
  • The ChangeDetectorRef provides methods to manually trigger change detection for a component. In this updateTitle() method, we update the title property and then call markForCheck() on the ChangeDetectorRef instance to explicitly mark the component for check and trigger change detection.
import { Component, ChangeDetectorRef } from '@angular/core';

@Component({
selector: 'app-example',
template: `
<h1>{{ title }}</h1>
<button (click)="updateTitle()">Update Title</button>
`
})
export class ExampleComponent {
title = 'Initial Title';

constructor(private cdr: ChangeDetectorRef) {}

updateTitle() {
this.title = 'Updated Title';

// Manually trigger change detection using detectChanges()
this.cdr.detectChanges();
}
}

Best Practices for DOM Manipulation in Angular 14

  • Limiting direct DOM manipulation when possible
  • Utilizing Angular’s data-binding for efficient updates
    Angular’s data-binding feature enables efficient updating of the DOM by automatically monitoring changes in component properties. This eliminates the necessity for manual DOM manipulation, streamlining the development process.
  • Minimizing unnecessary re-rendering with trackBy function
    When utilizing ngFor loops, incorporating the trackBy function can enhance performance by minimizing unnecessary re-rendering of DOM elements. The trackBy function enables Angular to distinguish unique elements based on a specified property, thereby optimizing the rendering process.
// component.html
<ul>
<li *ngFor="let item of items; trackBy: trackByFn">{{ item }}</li>
</ul>

Conclusion:

By following this guide, you’ll gain a solid understanding of the basic techniques for DOM manipulation in Angular 14. You’ll also learn how to consider performance and optimize your application by reducing re-rendering.

Hope you extracted some knowledge from it. In case of any doubt or if you just want to say Hi! feel free to reach me on LinkedIn or GitHub.

If you like the blog make sure to take a look at my YouTube channel for more amazing stuff.

--

--