Creating Reusable Component for Displaying Components like Ads Banner Using ViewContainerRef, ViewChild and RxJS.

Table of contents

When it comes to dynamic components, it can be challenging to ensure that they are displayed correctly. In addition, displaying ads on a website can be a task that requires flexibility.

To tackle these challenges, we will use ViewContainerRef and RxJS. ViewContainerRef is an Angular class that allows us to create and insert embedded views dynamically. RxJS is a library for reactive programming that allows us to create streams of data and subscribe to them in our components. Together, we will use these tools to create a reusable component for displaying ads.

Steps:

We create a bit of code in our component .html to display the banner

<div class="flex">
    <div class="border-red">

    </div>
</div>

add a little bit of styling to it

.flex {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;

    .border-red {
        height: 200px;
        width: 200px;
        border: 1px solid red;
        border-radius: 10px;
    }
}

we get this

Now that the banner UI is setup we head to the component.ts and import the decorators, types, and also Subscription from RxJS

import { Component, Input, ViewChild, ViewContainerRef,ViewEncapsulation } from '@angular/core';
import { interval, Subscription } from 'rxjs';
import { ComponentType } from '@angular/cdk/overlay';

So first, we want to be able to point our code to the dom where the actions should take place. we need to update our Html code to this

<div class="flex">
    <div class="border-red">
        <ng-container #container></ng-container>
    </div>
</div>

Make reference to the #container selector using the ViewChild decorator

@ViewChild('container', { read: ViewContainerRef }) 
container!: ViewContainerRef;

Proceed to create your component render function.

 ngOnInit(): void {
         this.container.createComponent(YourComponent)
 }

The piece of code above just renders a component.

we now need a piece of code that handles the creation of the components and gives us a callback on every completed cycle. checking against the components array coming from your input

 @Input()
 componentArray: ComponentType<{}>[] = [];

createComponent(componentIndex: number, _succ = () => { }) {
   this.container.clear()
   this.container.createComponent(this.componentArray[componentIndex])
        if (componentIndex === this.componentArray.length - 1) {
            this.subscription.unsubscribe()
            _succ()
            return
        }
}

to make it Observable we create an Observable function and subscribe to it considering the Loop, Interval and Delay of our render.

@Input() loop: boolean = true;
@Input() delay: number = 2000;
@Input() interval: number = 1000;
subscription : Subscription = new Subscription();



callObservable() {
const observable = interval(this.interval);
this.subscription = observable.subscribe(componentIndex =>  this.createComponent(componentIndex, () => {
     if (!this.loop) return
      if (this.delay) {
           setTimeout(() => {
              this.callObservable()
            }, this.delay);
       } else {
           this.callObservable()
       }
 }))
}

as we are working with inputs we can't do this in the ngOnInit() as by then our component would be undefined so let's modify our life cycle to this

 ngOnInit(): void {
        this.callObservable()
    }

    ngAfterViewInit(): void {
        this.container.createComponent(this.componentArray[0])
    }

Usage:

we import our components in the Html where needed

<lib-ad-banner
    [componentArray]="component"
    [interval]="interval"
    [delay]="delay"
>
</lib-ad-banner>

also declare the needed Inputs

interval: number = 1000;
delay: number = 2000;
component = [
  WidgetOneComponent,
  WidgetTwoComponent
]

Conclusion:

In this blog post, we have learned how to create reusable components for displaying components like ads banner using ViewContainerRef and RxJS. By following the steps outlined in this post, you can create flexible and maintainable solutions for displaying dynamic components in your Angular application. so our full component should look like this

import { Component, Input, ViewChild, ViewContainerRef, ViewEncapsulation } from '@angular/core';
import { interval, Subscription } from 'rxjs';
import { ComponentType } from '@angular/cdk/overlay';

@Component({
    selector: 'lib-ad-banner',
    templateUrl: './ad-banner.component.html',
    styleUrls: ['./ad-banner.component.scss'],
    encapsulation: ViewEncapsulation.None
})
export class AdBannerComponent {

    @Input() loop: boolean = true;
    @Input()
    componentArray: ComponentType<{}>[] = [];
    @Input() delay?: number;
    @Input() interval: number = 1000;
    @ViewChild('container', { read: ViewContainerRef }) container!: ViewContainerRef;

    subscription: Subscription = new Subscription();

    ngOnInit(): void {
        this.callObservable()
    }

    ngAfterViewInit(): void {
        this.container.createComponent(this.componentArray[0])
    }

    callObservable() {
        const observable = interval(this.interval);
        this.subscription = observable.subscribe(componentIndex => this.createComponent(componentIndex, () => {
            if (!this.loop) return
            if (this.delay) {
                setTimeout(() => {
                    this.callObservable()
                }, this.delay);
            } else {
                this.callObservable()
            }
        }))
    }

    createComponent(componentIndex: number, _succ = () => { }) {
        this.container.clear()
        this.container.createComponent(this.componentArray[componentIndex])
        if (componentIndex === this.componentArray.length - 1) {
            this.subscription.unsubscribe()
            _succ()
            return
        }
    }

}