How to Programmatically Render Directives in Angular +15

George-Valentin Hulpoi
George-Valentin Hulpoi2 min read 路 11 Dec 2023

Creating a directive in Angular without using the template, but dynamically using code was difficult until version 15. But with the help of the Directive composition API, we can make a workaround to dynamically create a directive.

Angular offers different ways to programmatically render components, you can read more about this in the article Programmatically rendering components.

However, unfortunately, Angular does not provide any built-in way to dynamically create a directive. Even if a component class extends a directive, these methods are not designed for dynamically creating directives.

There exist two options for dynamically creating a directive: one that utilizes the Directive Composition API, and alternatively, one that instantiates the class and handles the lifecycle and change detection phases manually.

There is no need to explain why it is a total hassle and non-intuitive to instantiate the directive class and bind it to the component's lifecycle and change detection.

A meme about solutions

Figure: A meme about solutions

However, the process can become very complicated when dealing with directives from third-party packages, where you have limited control over their implementation. For instance, using the RouterLink directive can be a complex task.

Now, let's delve into the concept of Directive Composition API. It is recommended to familiarize yourself with the documentation first and then revisit the tutorial.

Therefore, Directive Composition API allows us to attach directives to components. An innovative approach I came up with was to wrap the RouterLink directive in a component.

import {Component, ViewEncapsulation} from '@angular/core';
import {RouterLink} from '@angular/router';

@Component({
    selector: 'a',
    template: '<ng-content></ng-content>',
    standalone: true,
    encapsulation: ViewEncapsulation.None,
    hostDirectives: [
        {
            directive: RouterLink,
            inputs: ['routerLink', 'fragment', 'queryParamsHandling', 'queryParams'],
        },
    ],
})
export class AnchorComponent {}

As you can see, we add the directive to hostDirectives and connect the directive's inputs to the component's inputs.

Now let's try to programmatically render.

import {Component, ViewContainerRef, ViewChild, OnInit, Renderer2} from '@angular/core';
import {AnchorComponent} from './anchor';

@Component({
    selector: 'app',
    standalone: true,
    template: ` <ng-container #vc></ng-container> `,
})
export class App implements OnInit {
    @ViewChild('vc', {static: true, read: ViewContainerRef}) vc!: ViewContainerRef;

    constructor(private readonly renderer: Renderer2) {}

    ngOnInit(): void {
        const comp = this.vc.createComponent(AnchorComponent, {
            projectableNodes: [[this.renderer.createText('It works!!')]],
        });

        comp.setInput('routerLink', '/');
        comp.changeDetectorRef.detectChanges();
    }
}

In this Stackblitz you can see how it works 馃槂!

Happy codding!

WebAngular
SHARE

RELATED POSTS