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.
routerLink cannot be added after the content is already rendered but you can still achieve the desired result:
Create a href with dynamic data and give it a class:
`<a class="routerlink" href="${someDynamicUrl}">${someDynamicValue}</a>`
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}`]);
}
}
}
}
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
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With