Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Add element with RouterLink dynamically

When I put an anchor element in somewhere in an Angular component like this:

<a [routerLink]="['/LoggedIn/Profile']">Static Link</a>

everything is working fine. When clicking the link, the Angular router navigates to the target component.

Now, I would like to add the same link dynamically. Somewhere in my app I have a "notification component", its single responsibility is to display notifications.

The notification component does something like this:

<div [innerHTML]="notification.content"></div>

Where notification.content is a public string variable in the NotificationComponent class that contains the HTML to display.

The notification.content variable can contain something like:

<div>Click on this <a [routerLink]="['/LoggedIn/Profile']">Dynamic Link</a> please</div>

Everything works fine and shows up on my screen, but nothing happens when I click the dynamic link.

Is there a way to let the Angular router work with this dynamically added link?

PS: I know about DynamicComponentLoader, but I really need a more unrestricted solution where I can send all kinds of HTML to my notification component, with all kind of different links.

like image 832
Jeroen1984 Avatar asked Mar 30 '16 13:03

Jeroen1984


3 Answers

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}`]);
         }
       }
     }
    

    }

like image 151
AngularBoy Avatar answered Oct 19 '22 10:10

AngularBoy


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

like image 29
Günter Zöchbauer Avatar answered Oct 19 '22 09:10

Günter Zöchbauer


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

like image 1
Abdo Driowya Avatar answered Oct 19 '22 09:10

Abdo Driowya