Immutable Objects: When using OnPush, it's beneficial to work with immutable objects. If you need to modify data, create a new object or array instead of modifying the existing one. This helps Angular recognize changes more efficiently.
this.data = [...this.data, newElement]; // Using the spread operator for arrays

Input Properties: Ensure that your component's input properties are used correctly. When an input property changes, Angular triggers change detection for components using the OnPush strategy. If you're working with complex data structures, consider using @Input setters to handle changes.
import { Component, Input, ChangeDetectionStrategy } from '@angular/core';

@Component({
  selector: 'app-item',
  templateUrl: 'item.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class ItemComponent {
  private _data: any;

  @Input()
  set data(value: any) {
    this._data = value;
    // handle changes if needed
  }

  get data(): any {
    return this._data;
  }
}


Event Handling: Be cautious with event handling. When using OnPush, events outside of Angular's knowledge (e.g., events from third-party libraries) may not trigger change detection automatically. Use ChangeDetectorRef to manually mark the component for check.
import { Component, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';

@Component({
  selector: 'app-example',
  templateUrl: 'example.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class ExampleComponent {
  constructor(private cdr: ChangeDetectorRef) {}

  // Trigger change detection manually
  handleExternalEvent() {
    this.cdr.markForCheck();
  }
}


By using the OnPush change detection strategy and following these best practices, you can make your Angular application more efficient and responsive, especially in scenarios where components have a limited set of inputs or depend on immutable data.

2. Limit ngIf and ngFor in the Template

Limiting the use of ngIf and ngFor directives in your Angular templates is crucial for optimizing performance, as excessive use can lead to unnecessary rendering and affect the efficiency of change detection. Here are some best practices to follow:

  • Minimize ngIf and ngFor Nesting: Avoid deep nesting of ngIf and ngFor directives within your templates. The deeper the nesting, the more complex the change detection process becomes. Try to flatten your template structure when possible.
  • Filter Data Before Rendering: Instead of using ngFor to loop through all items and then applying conditions using ngIf, consider filtering your data in the component before rendering. This can reduce the number of elements in the template and improve rendering performance.

    <!-- Avoid -->
    <div *ngFor="let item of items" *ngIf="item.isValid">
      <!-- content -->
    </div>

    <!-- Prefer -->
    <div *ngFor="let item of validItems">
      <!-- content -->
    </div>


Use TrackBy with ngFor: When using ngFor, always provide a trackBy function to help Angular identify which items have changed. This can significantly improve the performance of rendering lists.
    <div *ngFor="let item of items; trackBy: trackByFn">
      <!-- content -->
    </div>


trackByFn(index, item) {
  return item.id; // Use a unique identifier
}


Avoid Excessive Use of Structural Directives: Be mindful of using too many structural directives (ngIf, ngFor, etc.) within a single template. Each structural directive introduces a potential change detection cycle, and having many of them can impact performance.

Lazy Load Components with ngIf: If you have complex or resource-intensive components, consider lazy-loading them using the ngIf directive. This way, the components will only be instantiated when they are needed.
<ng-container *ngIf="showComponent">
  <app-lazy-loaded-component></app-lazy-loaded-component>
</ng-container>


  • Paginate Large Lists: If dealing with large datasets, consider implementing pagination or virtual scrolling to load and render only the visible portion of the data. This can significantly improve the initial rendering time.
  • Profile and Optimize: Use Angular's built-in tools like Augury or browser developer tools to profile your application's performance. Identify components with heavy rendering and optimize accordingly.

3.  Lazy Loading Images
Lazy loading images is a technique that defers the loading of non-critical images until they are about to be displayed on the user's screen. This can significantly improve the initial page load time, especially for pages with a large number of images. Angular provides several ways to implement lazy loading of images. Here's a common approach:

Native Lazy Loading (HTML loading attribute): The HTML standard has introduced a loading attribute for the <img> element, which allows you to set the loading behavior of an image. The values can be "eager" (default), "lazy", or "auto". Setting it to "lazy" will enable lazy loading.
    <img src="image.jpg" alt="Description" loading="lazy">

The browser will then decide when to load the image based on its visibility in the viewport.

Angular Directives for Lazy Loading: You can use Angular directives for more control over lazy loading, especially if you need to perform custom actions when an image is loaded or when it enters the viewport.

a. Intersection Observer: Use the Intersection Observer API to detect when an element (such as an image) enters the viewport. Angular provides a directive named ng-lazyload-image that simplifies the integration with Intersection Observer.
npm install ng-lazyload-image

import { NgModule } from '@angular/core';
import { LazyLoadImageModule } from 'ng-lazyload-image';

@NgModule({
  imports: [LazyLoadImageModule],
  // ...
})
export class YourModule { }


<img [defaultImage]="'loading.gif'" [lazyLoad]="imagePath" alt="Description">

b. Custom Lazy Loading Directive: Alternatively, you can create a custom directive for lazy loading images. This approach provides more flexibility but requires a bit more code. You can use the Intersection Observer API or a library like lozad.js.
// lazy-load.directive.ts
import { Directive, ElementRef, Renderer2, OnInit } from '@angular/core';

@Directive({
  selector: '[appLazyLoad]'
})
export class LazyLoadDirective implements OnInit {

  constructor(private el: ElementRef, private renderer: Renderer2) { }

  ngOnInit() {
    const observer = new IntersectionObserver(entries => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          this.loadImage();
          observer.unobserve(entry.target);
        }
      });
    });

    observer.observe(this.el.nativeElement);
  }

  private loadImage() {
    const imgSrc = this.el.nativeElement.getAttribute('data-src');
    if (imgSrc) {
      this.renderer.setAttribute(this.el.nativeElement, 'src', imgSrc);
    }
  }
}


<img [appLazyLoad]="imagePath" data-src="loading.gif" alt="Description">

4. ng-container
Use the <ng-container> element to group elements without introducing additional elements to the DOM. It is a lightweight container that doesn't render as an HTML element.
<ng-container *ngIf="condition">
  <!-- content -->
</ng-container>


5. Avoid Heavy Computation in Templates
Keep your templates simple and avoid heavy computations or complex logic. If necessary, perform such operations in the component class before rendering.
Move Logic to the Component
Use Pure Pipes Judiciously
Memoization
NgIf and NgFor Directives

6. Use Angular Pipes Efficiently
Be cautious with Angular pipes, especially those that involve heavy computations. Pipes can have an impact on performance, so use them judiciously. Consider memoization techniques if a pipe's output is deterministic and costly.
// component.ts
export class MyComponent {
  heavyComputationResult: any;

  ngOnInit() {
    // Perform heavy computation here
    this.heavyComputationResult = /* result */;
  }
}


<!-- component.html -->
<div>{{ heavyComputationResult | formatData }}</div>


7. ngZone Awareness

Be aware of NgZone and its impact on change detection. If you're performing operations outside of Angular (e.g., third-party libraries or asynchronous operations), you may need to use NgZone.run to ensure that change detection is triggered appropriately.

8. Production Build
Always build your application for production using AOT compilation. This helps in optimizing and minifying the code for better performance.
ng build --prod

By applying these optimization techniques, you can enhance the performance of your Angular templates and create a more responsive user experience.

Improving the performance of your Angular application requires optimizing Angular templates. To maximize Angular templates, consider the following advice and best practices:

1. Apply Change Detection using OnPush
Implementing Angular's OnPush change detection approach can greatly enhance your application's performance. Angular is instructed by the OnPush strategy to only check for changes when an event within the component is triggered or when the input attributes of the component change. Reduced change detection cycles may arise from this, improving overall performance. This is how OnPush is used:1.

Configure a Change Detection Method: Set ChangeDetectionStrategy as the changeDetection property in your component decorator.OnPush:

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

@Component({
  selector: 'app-example',
  templateUrl: 'example.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class ExampleComponent {
  // component logic
}