Angular 15 – Custom Directive for Lazy Load Remote Images Tutorial

In this tutorial, we will discuss how to create a customer directive that will load images in lazy load manner also have an offset property to load loading distance.

Improve the performance of your Angular application by using a custom directive that loads images lazily. This directive allows you to delay loading images that are not visible on the screen, reducing the initial load time of your page and improving the user experience.

How to Create Custom Directive for Lazy Load Images?

To create a lazy load image directive in Angular 10 with an offset property to set how much before scroll the next image which is not in view should download, you can follow the below steps:

Step 1 – Create a new Angular directive using the CLI command:

ng generate directive lazy-load-image

Step 2 – In the generated lazy-load-image.directive.ts file, import the ElementRef and Renderer2 classes from @angular/core.

Step 3 – Define an @Input property called offset to allow the user to set the number of pixels before the image is in view to start downloading.

@Input() offset: number = 100;

Step 4 – Create a HostListener for the window:scroll event that will check if the image is in the viewport and load the image if it is.

@HostListener('window:scroll', ['$event'])
onScroll(event) {
  const rect = this.el.nativeElement.getBoundingClientRect();
  const windowHeight = window.innerHeight;
  const isImageVisible = rect.bottom >= 0 && rect.top - this.offset <= windowHeight;
  if (isImageVisible) {
    this.renderer.setAttribute(this.el.nativeElement, 'src', this.lazyLoadImage);
  }
}

Step 5 – In the ngOnInit lifecycle hook, set the src attribute to the placeholder image.

ngOnInit() {
  this.renderer.setAttribute(this.el.nativeElement, 'src', this.placeholderImage);
}

Step 6 – Define two @Input properties to allow the user to set the placeholder and lazy load images.

@Input() placeholderImage: string = 'assets/placeholder.png';
@Input() lazyLoadImage: string;

Step 7 – In the HTML template, add the lazy-load-image directive to the <img> tag and set the lazyLoadImage property to the image URL.

<div *ngFor="let imageUrl of images">
    <img lazy-load-image [lazyLoadImage]="imageUrl" [offset]="100">
</div>

The final lazy-load-image.directive.ts file should look like this:

import {
  Directive,
  ElementRef,
  Renderer2,
  HostListener,
  Input,
  OnInit,
} from '@angular/core';

@Directive({
  selector: '[lazy-load-image]',
})
export class LazyLoadImageDirective implements OnInit {
  @Input() placeholderImage: string = 'assets/placeholder.png';
  @Input() lazyLoadImage!: string;
  @Input() offset: number = 100;
  private isImageLoaded: boolean = false;

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

  ngOnInit() {
    const rect = this.el.nativeElement.getBoundingClientRect();
    const windowHeight = window.innerHeight;
    const isImageVisible =
      rect.bottom >= 0 && rect.top - this.offset <= windowHeight;
    if (isImageVisible) {
      this.loadImage();
    } else {
      this.renderer.setAttribute(
        this.el.nativeElement,
        'src',
        this.placeholderImage
      );
    }
  }

  @HostListener('window:scroll', ['$event'])
  onScroll(event: any) {
    if (!this.isImageLoaded) {
      const rect = this.el.nativeElement.getBoundingClientRect();
      const windowHeight = window.innerHeight;
      const isImageVisible =
        rect.bottom >= 0 && rect.top - this.offset <= windowHeight;
      if (isImageVisible) {
        this.loadImage();
      }
    }
  }

  private loadImage() {
    this.renderer.setAttribute(
      this.el.nativeElement,
      'src',
      this.lazyLoadImage
    );
    this.isImageLoaded = true;
  }
}

In the component class, you can define the images object as follows for example:

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

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  images: string[] = [];
  constructor() {
    for (let index = 0; index < 150; index++) {
      this.images.push('https://picsum.photos/200');
    }
  }
}

 

Conclusion

With this custom directive, you can optimize the loading of images in your Angular 10 application and improve its performance. By delaying loading images that are not visible on the screen, you can reduce the initial load time of your page, improve the user experience, and boost your website’s search engine rankings.

Leave a Comment

Your email address will not be published. Required fields are marked *