Angular2 way of converting plain text to url (anch

2019-02-08 22:58发布

问题:

I sometimes have a component that can receive text like this:

text www.website.com

But I would like to convert it to a url if it is a link. Like this.

text www.website.com

I read this SO answer that suggests using 3rd party libs such as anchorme. Is there anywway to do it the angular2 way?

回答1:

There are numerous problems with using simple regexes to modify HTML content.

Here's an approach that uses the linkifyjs module, which you need to npm install. Do notice that the input is considered plain text, while output is HTML text.

import { Pipe, PipeTransform } from '@angular/core';
import linkifyStr from 'linkifyjs/string';

@Pipe({name: 'linkify'})
export class LinkifyPipe implements PipeTransform {
  transform(str: string): string {
    return str ? linkifyStr(str, {target: '_system'}) : str;
  }
}

NB: If you need to specify the target attributes, add eg. {target: '_system'} as a second parameter to linkifyStr.



回答2:

Okay so to make a pipe you would make a pipe component consisting of

  import { Pipe, PipeTransform } from '@angular/core';



    @Pipe({name: 'linkify'})
    export class LinkifyPipe implements PipeTransform {
      transform(link: string): string {
        return this.linkify(link);
      }

      private linkify(plainText): string{
        let replacedText;
        let replacePattern1;
        let replacePattern2;
        let replacePattern3;

        //URLs starting with http://, https://, or ftp://
        replacePattern1 = /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim;
        replacedText = plainText.replace(replacePattern1, '<a href="$1" target="_blank">$1</a>');

        //URLs starting with "www." (without // before it, or it'd re-link the ones done above).
        replacePattern2 = /(^|[^\/])(www\.[\S]+(\b|$))/gim;
        replacedText = replacedText.replace(replacePattern2, '$1<a href="http://$2" target="_blank">$2</a>');

        //Change email addresses to mailto:: links.
        replacePattern3 = /(([a-zA-Z0-9\-\_\.])+@[a-zA-Z\_]+?(\.[a-zA-Z]{2,6})+)/gim;
        replacedText = replacedText.replace(replacePattern3, '<a href="mailto:$1">$1</a>');

        return replacedText;
       }
    }

then import this like u would a directive, pass it to the

pipes: [LinkifyPipe]

and interpolate like this

{{url | linkify}}


回答3:

you should use this pipe in this way:

<div [innerHtml]="note.title | linkify"></div>



回答4:

You should check angular-linky https://github.com/dzonatan/angular-linky

<span [innerHTML]="yourText | linky"></span>


回答5:

I have searched for a solution but to no avail. I needed to extent the requirement to handle Angular routing [routerLink] and external links with [innerHTML] without the need for 3rd party libs.

My solution (summary):

Angular 2, Angular 5 - User click events/event handling for dynamic [innerHTML] generated content using pipe and directive to generate and converting plain text to click urls e.g #hashtags, @Handle, @Mention, #routerLink #href and #mailto etc

Plunker demo: https://embed.plnkr.co/68lZFY/

AppComponent

import { Component, NgModule, VERSION} from '@angular/core';
import { BrowserModule} from '@angular/platform-browser';

@Component({
    selector: 'my-app',
    template: `<h1>Angular - Dynamic content click event</h1>
                        <p>Angular 2, Angular 5, Typescript - User click events/event handling for dynamic [innerHTML] generated content using pipe and directive to generate and converting plain text to click urls e.g #hashtags,  @Handle, @Mention, #routerLink #href and #mailto etc</p>
                        <ul>
                            <li *ngFor="let item of theList; let $index=index;" [innerHTML]="item | parseUrl" [dynamicContent]="currentView"></li>
                        <ul>`
})
export class AppComponent {
     theList:Array;

        constructor() {
                this.theList = [
                    'Lorem ipsum dolor sit amet, consectetur @adet dolore magna aliqua. Ut enim ad minim veniam',
                    'Lorem ipsum dolor sit amet, consectetur adipiscing http://google.com sed do eiusmod tempor #incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam',
                    'Lorem http://google.com ipsum dolor #sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt gna aliqua. Ut enim ad minim veniam',
                    'Lorem ipsum @dolor sit amet, consectetur @adipiscing elit, sed do eiusmod @tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam',
                    'Lorem ipsum dolor sit amet, smod tempor incididunt #ut labore et dolore @magna #aliqua. Ut enim ad minim veniam',
                    'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam',
                    'Lorem ipsum @dolor sit amet, #consectetur adipiscing elit, sed do eiusmod tempor http://google.com enim ad minim veniam'
                ];
        }

}

Directive

import { Directive, ElementRef, HostListener, Input } from '@angular/core';

@Directive({
    selector: '[dynamicContent]'
})
export class DynamicContent {

    constructor(private el: ElementRef) { }

    @Input('dynamicContent') dynamicContent: string;

    @HostListener('click', ['$event']) onClick(e) {

        if (e.target.classList.contains('handle-link')) {
            let link: string = e.target.innerHTML;

            event.preventDefault();
            event.stopPropagation();

            alert("/search/handle/" + link.trim());

            //this.router.navigateByUrl("/search/handle/" + link.trim(), { skipLocationChange: false });

        } else if (e.target.classList.contains('hashtag-link')) {
            let link: string = e.target.innerHTML;

            event.preventDefault();
            event.stopPropagation();

             alert("/search/hashtag/" + link.trim());

            //this.router.navigateByUrl("/search/hashtag/" + link.trim(), { skipLocationChange: false }); 

        }

    }

}

Pipe

export class ParseUrl implements PipeTransform {

    urls: any = /(\b(https?|http|ftp|ftps|Https|rtsp|Rtsp):\/\/[A-Z0-9+&@#\/%?=~_|!:,.;-]*[-A-Z0-9+&@#\/%=~_|])/gim; // Find/Replace URL's in text  
    hashtags: any = /(^|\s)(#[a-z\d][\w-]*)/ig; // Find/Replace #hashtags in text   
    mentions: any = /(^|\s)(@[a-z\d][\w-]*)/ig; // Find/Replace @Handle/Mentions in text    
    emails: any = /(\S+@\S+\.\S+)/gim; // Find/Replace email addresses in text

    transform(text: string) {
        return this.parseUrl(text);
    }

    private parseUrl(text: string) {
        // Find/Replace URL's in text
        if (text.match(this.urls)) {
                text = text.replace(this.urls, function replacer($1, $2, $3) {
                        let url: any = $1;
                        let urlClean: any = url.replace("" + $3 + "://", "");

                        return "<a href=\"" + url + "\" target=\"_blank\">" + urlClean + "</a>";
                });
        }

        // Find/Replace @Handle/Mentions in text
        if (text.match(this.hashtags)) {
            text = text.replace(this.hashtags, "<a href=\"/search/hashtag/$2\" class=\"hashtag-link\">$1$2</a>");
        }

        // Find/Replace #hashtags in text
        if (text.match(this.mentions)) {
            text = text.replace(this.mentions, "<a href=\"/search/handle/$2\" class=\"handle-link\">$1$2</a>");
        }

        // Find/Replace email addresses in text
        if (text.match(this.emails)) {
                text = text.replace(this.emails, "<a href=\"mailto:$1\">$1</a>");
        }

        return text;
    }  
}


回答6:

Ok this is how I did it leaving answer hope it helps someone else:

So I'm using a function to linkify my plaint text

private linkify(plainText): string{
    let replacedText;
    let replacePattern1;
    let replacePattern2;
    let replacePattern3;

    //URLs starting with http://, https://, or ftp://
    replacePattern1 = /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim;
    replacedText = plainText.replace(replacePattern1, '<a href="$1" target="_blank">$1</a>');

    //URLs starting with "www." (without // before it, or it'd re-link the ones done above).
    replacePattern2 = /(^|[^\/])(www\.[\S]+(\b|$))/gim;
    replacedText = replacedText.replace(replacePattern2, '$1<a href="http://$2" target="_blank">$2</a>');

    //Change email addresses to mailto:: links.
    replacePattern3 = /(([a-zA-Z0-9\-\_\.])+@[a-zA-Z\_]+?(\.[a-zA-Z]{2,6})+)/gim;
    replacedText = replacedText.replace(replacePattern3, '<a href="mailto:$1">$1</a>');

    return replacedText;
}

But this returns a string with html encoding so if I use in with <p>{{example}}</p> it will return full encoding (inlcuding anchor tags and html).

So now I use angular2 builtin html binding:

This gives me the solution