Angular 5. How to dynamically add links to strings containing a certain tag. like '@'

10,946

Solution 1

You have to use the innerHTML property to render the provided string as HTML, so instead of

<p> {{post.content}} </p>

You should use

<p [innerHTML]="post.content"></p>

Demo

However, this method is not safe and is prone to XSS if not handled properly,


The recommended method: Create a pipe with DOM Sanitization

linkify.pipe.ts

import { Pipe, PipeTransform } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';

@Pipe({
  name: 'linkify'
})
export class LinkifyPipe implements PipeTransform {

  constructor(private _domSanitizer: DomSanitizer) {}

  transform(value: any, args?: any): any {
    return this._domSanitizer.bypassSecurityTrustHtml(this.stylize(value));
  }

  // Modify this method according to your custom logic
  private stylize(text: string): string {
    let stylizedText: string = '';
    if (text && text.length > 0) {
      for (let t of text.split(" ")) {
        if (t.startsWith("@") && t.length>1)
          stylizedText += `<a href="#${t.substring(1)}">${t}</a> `;
        else
          stylizedText += t + " ";
      }
      return stylizedText;
    }
    else return text;
  }

}

You can modify the stylize method according to your logic.

Usage:

<p [innerHTML]="sample | linkify"></p>

Demo Stackblitz

Solution 2

cyberpirate92's answer is great, though it has one issue: if a link is at the end of the line, the \n will cause the next word on the next line will get captured as well.

I adapted their answer to work on http links as well:

import { Pipe, PipeTransform } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';

@Pipe({ name: 'linkify' })
export class LinkifyPipe implements PipeTransform {

  constructor(private _domSanitizer: DomSanitizer) {}

  transform(value: any, args?: any): any {
    return this._domSanitizer.bypassSecurityTrustHtml(this.stylize(value));
  }

  // Modify this method according to your custom logic
  private stylize(text: string): string {
    let stylizedText: string = '';
    if (text && text.length > 0) {
      for (let line of text.split("\n")) {
        for (let t of line.split(" ")) {
          if (t.startsWith("http") && t.length>7) {  
            stylizedText += `<a href="${t}">${t}</a> `;
          }
          else
            stylizedText += t + " ";
        }
        stylizedText += '<br>';
      }
      return stylizedText;
    }
    else return text;
  }

}
Share:
10,946
Jonas Praem
Author by

Jonas Praem

Updated on June 14, 2022

Comments

  • Jonas Praem
    Jonas Praem almost 2 years

    I have a template view which looks like this

    <div class="post-content">
          <p>{{ post.content }}</p>
    </div>
    

    where post.content is a type of string.

    The string may or may not contain one or more @ tag which references different users. example: '@username'. I want to make this tag clickable with a link. Sort of insert it as an anchor tag:

    <a>@username</a>
    

    so far I have tried to string manipulate it manually, and inserting the anchor tag inside the string. However this just shows up as plain text in the view.

    How do I go about this in Angular 5?

    • cyberpirate92
      cyberpirate92 over 6 years
      try <p [innerHTML]="post.content"></p>
    • Patrick Kelleter
      Patrick Kelleter over 6 years
      yeah but keep in mind, that if you use innerHtml in order to generate html on the fly, you will have to make sure to be safe against xss attacks
  • Diskdrive
    Diskdrive almost 5 years
    this works. The only problem I've found is that the link actually re-loads the angular SPA page.
  • user3519066
    user3519066 over 3 years
    you can use pipe and call above function.ex <span [innerHTML]="text | safe: 'html'"></span>