How do I use decoupled routing information within content, managed by a backend?

In my setup, there is a CMS as a backend that manages content and an ionic app as a front end. Normally, I would implement the navigation via routerLink values and for most cases like menus or special linking components that will work, no matter how the actual routing information looks like. But what should I do, when the linking element is part of the content?

Let’s say, I have a text block and somewhere in the middle of the text, I have a link. I manage the text block in the CMS which usually only uses plain text, simplified html or normal html. With this, I would normally manage the link here by adding it as a link with an a tag and a href value.

The front end can read load this text block and even consider it to be html. But then, I don’t have a smooth router navgiation but a new page load.

Is there a standard way of “translating” inline links to router navigation?

I know that the situation is a bit weird here, because typically, routes should be part of the front end and the backend shouldn’t care about them, but if I manage the content via the backend, I will have this mix, where I have inline links that will be entered within the backend.

I didn’t test this so far, but I played around with ChatGPT and got the answer that I should try:

<!-- In your component template -->
<div [innerHtml]="htmlContent" [routerLink]="link.href" routerLinkActive="active" #linkRef></div>

Togehter with:

// In your component class
export class MyComponent {
  htmlContent: string;

  constructor(private router: Router) {
    // Retrieve the HTML content from your backend, 
    // which includes the links with href attributes
    this.htmlContent = '<p>Some text <a href="/route1">Link 1</a> More text <a href="/route2">Link 2</a></p>';
  }

  @ViewChildren('linkRef', { read: RouterLinkWithHref }) links: QueryList<RouterLinkWithHref>;

  ngAfterViewInit() {
    this.links.forEach(link => {
      link.href = this.router.serializeUrl(link.urlTree);
    });
  }
}

I will update the answer as soon as I tried.

The above approach didn’t work. The actual solution was to write a directive that intercepts the click event.

@Directive({
  standalone: true,
  selector: '[appLinkify]',
})
export class LinkifyDirective {
  constructor(private router: Router) {}

  @HostListener('click', ['$event'])
  public async onClick(event: PointerEvent & { target: HTMLAnchorElement }): Promise<void> {
    const href: string | null = event.target.getAttribute('href');

    if (!href || href.startsWith('http')) {
      return;
    }

    event.preventDefault();
    await this.router.navigateByUrl(href);
  }
}

In the current setup, I add this directive to all the components loading html content from the backend.