1

I have a long piece of text to display in my app and so I want to use a string of HTML.

// component
description: string = `<p>Hello world. <a routerLink="/home">Click here</a> to go to the home page.</p>`;

// template
<div class="content">
  <div innerHTML="description"></div>
</div>

This renders correctly, except Angular strips out the routerLink piece and the link doesn't work. I have tried using a custom pipe to pass the string through the DomSanitizer function bypassSecurityTrustHtml(), but Angular doesn't pick up on the syntax and does not treat it as a routerLink.

Stewart
  • 1,659
  • 4
  • 23
  • 35
  • 1
    a word of advice, in 2019, if you ever find yourself writing `innerHTML` you're doing it wrong. – Stavm Apr 29 '19 at 18:13
  • @Stavm Care to explain why? That is required for embed string-based HTML in Angular as I understand it. – Stewart Apr 29 '19 at 18:15
  • what is the purpose of using routerLink here? why don't you just put a regular [href]? – Mauro Aguilar Apr 29 '19 at 18:16
  • @MauroAguilar Same reason anyone would use routerLink: to activate the path using the Angular router: https://angular.io/guide/router – Stewart Apr 29 '19 at 18:18
  • You can't. The angular directives must be part of the template to be compiled by the Angular compiler. – JB Nizet Apr 29 '19 at 18:19
  • @Stewart but I think Angular compiler won't understand that because the dynamic rendering of HTML will happen at runtime. Have you tried href ? – Mauro Aguilar Apr 29 '19 at 18:19
  • @MauroAguilar href "works" but does not use the Angular router. It reloads the whole app. – Stewart Apr 29 '19 at 18:27
  • Possible duplicate of [Best way to pass angular routerLink URL's in an HTML string](https://stackoverflow.com/questions/55873569/best-way-to-pass-angular-routerlink-urls-in-an-html-string) – Reactgular Apr 29 '19 at 19:16

4 Answers4

1

The short, useless answer is "You can't".


At the end of the day, Angular as a framework is about generating and updating the DOM in an efficient and understandable way. By using the innerHtml binding, you're telling Angular "I know what I'm doing, just put this string in the DOM" - which it happily does after sanitizing truly dangerous things (like <script> tags). Since you're telling Angular exactly what should be in the DOM, it doesn't bother with any additional processing (like parsing directives).


So, how do you make this work?

Instead of using innerHtml you need to use an actual component. This will likely be a pain to re-work, but it allows you to use Angular bindings and custom styles.


If you need help with that specific re-work, I recommend you ask a new question with that information

Vlad274
  • 6,514
  • 2
  • 32
  • 44
0

Instead, you can define a (click) method on which you can navigate to the required route.

AFAIK, dynamically rendered templates won't bind routerLink properties

Saksham
  • 9,037
  • 7
  • 45
  • 73
0

If you definitely can't put this in an Angular template you would need to query the anchors dynamically and instantiate the Router provider to navigate programmatically. Something like:

constructor(private router: Router, private ref: ElementRef) {}

ngAfterViewInit() {
  const anchors = [...this.ref.nativeElement.querySelectorAll('a')];
  anchors.forEach((a) => a.addEventListener('click', (e) => {
    e.preventDefault();
    this.router.navigate([a.href]);
  }));
}

Kind of ugly though...

Mauro Aguilar
  • 1,093
  • 13
  • 22
0

I want to make a not here that if you have a link with hre="/foo" and you want to parse, just using this.router.navigate([a.href]) will not work properly because a.href will be evaluated to the entire URL, meaning: http://localhost:4200/foo - this is not what you want since that won't match an angular route!

Here's what I ended up doing, pulling from answers in this question:

First, I marked all the links that I needed to be parsed like this with rel="ng" to separate them from any other links that don't need to be parsed

this.myHtmlElement.nativeElement.querySelectorAll<HTMLAnchorElement>('a[rel="ng"]').forEach((link) => {
  link.addEventListener('click', (evt) => {
    evt.preventDefault();
    void this.router.navigate([link.attributes.getNamedItem('href')?.value]);
  });
});
Chris Barr
  • 29,851
  • 23
  • 95
  • 135