Dynamic Image Loading With Angular

The simplest way to lazy load DOM elements within page with Angular

Let’s assume that there are 100+ images on your page, and you only see two or three of them when the page loads. Then, why browsers load 100 images when the page initializes?

There is no way not to load image as long as there are <img> tag in your html. This means you have to change html not to have image tags and change html dynamically to avoid initial image loading. However, changing html dynamically is not an easy task, and it requires a lot of scripting.

Fortunately, Angular has its own way to do this task using ng-container and ngTemplateOutlet . The way to achieve to this goal is also to use HTML5 IntersectionObserver. I am not going to explain how ng-container and ngTemplateOutlet works. About those, please learn from the following sites; Angular — Structural Directives and The Complete Guide To Angular Templates

To make a long story short. This is the angular html code, and this is the working demo; https://ngui-in-view-basic.stackblitz.io/

<hr/>

List Of Images

<hr/> <div *ngFor="let i of numArr">

<ngui-in-view>

<img *ngIf src="https://picsum.photos/800/300?image={{i}}>

</ngui-in-view>

</div>

The full example is here at https://stackblitz.com/edit/ngui-in-view-basic

The above code will not load any image unless you see it. There are two things to notice; <ngui-in-view> and *ngIf.

<ngui-in-view> : A custom component to show the contents only if in view.

: A custom component to show the contents only if in view. *ngIf : this will generate ng-template internally, which Angular does not render unless condition meets.

First, lets take a look at <ngui-in-view> component.

To explain the above code,

ngOnInit(): void {

if (isPlatformBrowser(this.platformId)) {

this.observer = new

IntersectionObserver(this.handleIntersect.bind(this), this.options);

this.observer.observe(this.element.nativeElement);

}

}

When a component is initialized, it will register an IntersectionObserver, so that when element is visible, it calls the callback handleIntersect .

handleIntersect(entries, observer): void {

entries.forEach((entry: IntersectionObserverEntry) => {

if (entry.isIntersecting) {

this.inView = true;

this.inView$.emit(entry);

// this.defaultInViewHandler(entry);

} else {

this.notInView$.emit(entry);

}

});

}

The observer callback handleIntersect sets inView to true and fires inView$ event when the element is visible and fires notInView$ event when the element is not visible.

@ContentChild(TemplateRef) template: TemplateRef<any>;

It’s the reference to <ng-template> tag. In this example it’s *ngIf .

<div *ngIf>1<div> is the same as <ng-template [ngIf]="">1<ng-template> .

@Component({

selector: 'ngui-in-view',

template: `

<ng-container *ngIf="inView" [ngTemplateOutlet]="template">

</ng-container>

`

})

ng-container will show the template; <img *ngIf src=”https://picsum.photos/800/300?image={{i}}" height=”33%” , specified by ngTemplateOutlet when inView is true, which means when element is visible.

Visiting app.component.html again, there are 100s of images, and only visible elements will be downloaded and displayed.

<div *ngFor="let i of numArr">

<ngui-in-view>

<img *ngIf src="https://picsum.photos/800/300?image={{i}}>

</ngui-in-view>

</div>

You can see the full code here. https://stackblitz.com/edit/ngui-in-view-basic

Next article will deal with virtual list, not infinite list, which will expand this concept. You can see the preview of virtual list at here

Happy coding,

and happy Angular

Allen Kim

Do you think this useful? If so please;

* Clap 👏 button below️ to let others to see this too.

* Follow Allen on Twitter (@allenhwkim)