Add element with RouterLink dynamically

routerLink cannot be added after the content is already rendered but you can still achieve the desired result:

  1. Create a href with dynamic data and give it a class:

    `<a class="routerlink" href="${someDynamicUrl}">${someDynamicValue}</a>`
    
  2. Add a HostListener to app.component that listens for the click and uses the router to navigate

    @HostListener('document:click', ['$event'])
    public handleClick(event: Event): void {
     if (event.target instanceof HTMLAnchorElement) {
       const element = event.target as HTMLAnchorElement;
       if (element.className === 'routerlink') {
         event.preventDefault();
         const route = element?.getAttribute('href');
         if (route) {
           this.router.navigate([`/${route}`]);
         }
       }
     }
    

    }


Since angular 9, AOT is the default recommended way to compile angular projects. Unlike JIT, AOT doesn't hold an instance for the compiler at runtime, which means you can't dynamically compile angular code. It's possible to disable AOT in angular 9, but it's not recommended as your bundle size will be bigger and your application slower.

The way I solve this is by adding a click listener at runtime using renderer api, preventing the default behavior of urls and calling angular router

import { Directive, ElementRef, OnDestroy, OnInit, Renderer2 } from '@angular/core';
import { Router } from '@angular/router';

@Directive({
  selector: '[hrefToRouterLink]'
})
export class HrefToRouterLinkDirective implements OnInit, OnDestroy {
  private _listeners: { destroy: () => void }[] = [];

  constructor(private _router: Router, 
  private _el: ElementRef, 
  private _renderer: Renderer2) {
  }

  ngOnInit() {
    // TODO how to guarantee this directive running after all other directives without setTimeout?
    setTimeout(() => {
      const links = this._el.nativeElement.querySelectorAll('a');
      links.forEach(link => {
        this._renderer.setAttribute(link, 'routerLink', link?.getAttribute('href'));
        const destroyListener = this._renderer.listen(link, 'click',
          (_event) => {
            _event.preventDefault();
            _event.stopPropagation();
            this._router.navigateByUrl(link?.getAttribute('href'));
          });
        this._listeners.push({ destroy: destroyListener });
      });
    }, 0);
  }

  ngOnDestroy(): void {
    this._listeners?.forEach(listener => listener.destroy());
    this._listeners = null;
  }

}

You can find an example here : https://stackblitz.com/edit/angular-dynamic-routerlink-2

Obviously the method explained above work for both JIT & AOT, but If you are still using JIT and want to dynamically compile component (which may help solve other problems) . You can find an example here : https://stackblitz.com/edit/angular-dynamic-routerlink-1

Used resources :

https://stackoverflow.com/a/35082441/6209801

https://indepth.dev/here-is-what-you-need-to-know-about-dynamic-components-in-angular


routerLink is a directive. Directives and Components are not created for HTML that is added using [innerHTML]. This HTML is not process by Angular in any way.

The recommended way is to not use [innerHTML] but DynamicComponentLoaderViewContainerRef.createComponent where you wrap the HTML in a component and add it dynamically.

For an example see Angular 2 dynamic tabs with user-click chosen components