We are working on a decoupled project with Drupal as our backend and Angular as our front-end. Everything is nearing completion but certain (dynamic, as in created in Drupal) pages require links to different pages of the angular app.
For example:
- An editor creates an FAQ question in Drupal with an answer
containing a link to the Pricing page of the angular app.
(Q: How much does this service cost? A: Check out our [pricing] page)
- User on frontend opens FAQ page
- Angular loads our FAQ page (html) from Drupal via REST
- User clicks link mentioned in step 1
What happens:
The entire page gets reloaded, losing any processes that were currently going on (i.e. content playing in a player), and having the user look at our loading screen for a bit again.
What we would like to happen:
We would like to trigger the router to go to an internal URL so we don't lose our SPA feeling.
We have tried to replicate this by using Dynamic Components, but since we have no idea before hand what the content looks like it's hard to implement this.
Is there an official way (or workaround perhaps) to fix this ? We imagined we could do it by either parsing any links we get from our CMS, or making a custom formatting in our CKeditor to parse. But once we get to the angular side, we don't know how we should insert our HTML with the links now working internally.
It's been a while since you posted but I didn't find a solution myself so wanted to create this as a future reference.
We solved this problem by using a Directive that we use on the html like so:
<div [innerHTML]='contentFromCMS' appRouteDirective></div>
The Directive captures all clicks and checks if the target is an tag with a href element. If the href is relative, use the router. If it contains hostname (set your own) it gets the path and uses the router. Otherwise it opens in a new window.
import { Directive, ElementRef, HostListener } from '@angular/core';
import { Router } from '@angular/router';
declare let window: any;
declare let document: any;
@Directive({
selector: '[appRouteDirective]'
})
export class RouterLinkDirective {
public hostname = '.hygglo.se'; // The starting dot avoids email links as well
constructor(private el: ElementRef, private router: Router) {}
@HostListener('click', ['$event'])
public onClick(e) {
const href = e.target.getAttribute('href');
if (e.target.tagName === 'A' && href) {
e.preventDefault();
if (this.isLocalLink(href)) {
this.router.navigate([this.getPathFromURL(href)]);
} else {
window.open(e.target.href);
}
}
}
public getPathFromURL(url: string) {
let el = document.createElement('a');
el.href = url;
return el.pathname;
}
public isLocalLink(uri: string) {
if (uri && (uri.startsWith('/') || uri.includes(this.hostname))) {
return true;
} else {
return false;
}
}
}