Skip to content

Instantly share code, notes, and snippets.

@adamrecsko
Created May 1, 2016 20:28
Show Gist options
  • Select an option

  • Save adamrecsko/0f28f474eca63e0279455476cc11eca7 to your computer and use it in GitHub Desktop.

Select an option

Save adamrecsko/0f28f474eca63e0279455476cc11eca7 to your computer and use it in GitHub Desktop.
Angular2 text highlight pipe
import {PipeTransform, Pipe} from 'angular2/core';
@Pipe({ name: 'highlight' })
export class HighLightPipe implements PipeTransform {
transform(text: string, [search]): string {
return search ? text.replace(new RegExp(search, 'i'), `<span class="highlight">${search}</span>`) : text;
}
}
/** Usage:
* <input type="text" [(ngModel)]="filter">
* <div [innerHTML]="myAwesomeText | highlight : filter"></div>
*
*/
@andreialecu

andreialecu commented Oct 14, 2016

Copy link
Copy Markdown

This seems to be the top link in Google, so here's an improved version for Angular 2.0 final. The original version had a problem with capitalized characters, replacing them with their lower case equivalent and also didn't escape regex control chars, resulting in other bugs.

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

@Pipe({ name: 'highlight' })
export class HighlightPipe implements PipeTransform {
  transform(text: string, search): string {
    var pattern = search.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
    pattern = pattern.split(' ').filter((t) => {
      return t.length > 0;
    }).join('|');
    var regex = new RegExp(pattern, 'gi');

    return search ? text.replace(regex, (match) => `<span class="highlight">${match}</span>`) : text;
  }
}

ghost commented Oct 20, 2016

Copy link
Copy Markdown

Nice one, thanks! :)

@meiriko

meiriko commented Oct 27, 2016

Copy link
Copy Markdown

Nice indeed.
Note that the code will fail with error if the search param is null (due to the call to .replace()). It might be simpler to add a simple check instead of the ternary operator at the end.

@koelle25

koelle25 commented Nov 15, 2016

Copy link
Copy Markdown

That's exactly what I was searching for - excellent!
Only problem:
How can I style the span.highlight with a component-inheritent stylesheet? (styleUrl: [...])
Currently the styling is not applied because angular doesn't add the html-property (_ngcontent-...) to the Element when using [innerHtml]-binding... 😕

Edit: Ah, found a workaround (as described on StackOverflow)
:host >>> mySelector { /* some styling */ }

@CarstenHouweling

Copy link
Copy Markdown

<span [innerHTML]="text | highlight: search">

works perfectly, thanks a lot.

@vhogemann

Copy link
Copy Markdown

Awesome! Thank you!

@leomayer

Copy link
Copy Markdown

I change tiny bits to be compatible with tslint-checks and work in some undefined cases - thx for the code :-)

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

@Pipe({ name: 'highlight' })
export class HighlightPipe implements PipeTransform {
  transform(text: string, search): string {
    if (search && text) {
      let pattern = search.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');
      pattern = pattern.split(' ').filter((t) => {
        return t.length > 0;
      }).join('|');
      const regex = new RegExp(pattern, 'gi');

      return text.replace(regex, (match) => `<span class="search-highlight">${match}</span>`);
    } else {
      return text;
    }
  }
}

@manas89

manas89 commented Aug 1, 2017

Copy link
Copy Markdown

This what I am looking for. Workers Perfectly fine.

@jculverwell

jculverwell commented Aug 17, 2017

Copy link
Copy Markdown

To get the styling to work you need to add one of the styles below (depends on whether you are using leomayer or original version)

:host ::ng-deep .highlight{
  background-color: #F2E366;
}

Or

:host ::ng-deep .search-highlight{
  background-color: #F2E366;
}

@nglasantha

Copy link
Copy Markdown

Thanks works fine

@nooot77

nooot77 commented Oct 8, 2017

Copy link
Copy Markdown

but what about : "WARNING: sanitizing HTML stripped some content (see http://g.co/ng/security#xss)."

@jboarman-ap

Copy link
Copy Markdown

@nooot77
For me, the key phrase from Angular's documentation regarding XSS vulnerabilities is "binding a value that an attacker might control into innerHTML normally causes an XSS vulnerability". If you must display html from an untrusted source, ng still attempts to mitigate the risk by stripping <script> tags. So, while it might strip some content, it's hopefully a good thing when it does. I think the warning would be to indicate that if you intend to insert scripts, then you should do it a more traditional way. What do you think? Does that make any sense given the context you are seeing this warning?

@teslovych

Copy link
Copy Markdown

@nooot77

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

@Pipe({ name: 'highlight' })
export class HighlightPipe implements PipeTransform {
    constructor(public sanitizer: DomSanitizer) {
    }
    transform(text: string, search): SafeHtml {
        if (search && text) {
            let pattern = search.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');
            pattern = pattern.split(' ').filter((t) => {
                return t.length > 0;
            }).join('|');
            const regex = new RegExp(pattern, 'gi');
            return this.sanitizer.bypassSecurityTrustHtml(
                text.replace(regex, (match) => `<span class="search-highlight">${match}</span>`)
            );

        } else {
            return text;
        }
    }
}

@Deepakchawla

Copy link
Copy Markdown

Thanks it works fine...

@ip7e

ip7e commented Dec 4, 2017

Copy link
Copy Markdown

Test here, if you care about coverage:

import { DomSanitizer } from '@angular/platform-browser'
import { TestBed, inject } from '@angular/core/testing';

import { HighlightPipe } from './highlight.pipe';

fdescribe('AutoComplete Component - Highlight pipe', () => {
    let pipe: HighlightPipe;

    beforeEach(() => {
        TestBed.configureTestingModule({
            providers: [ {
                provide: DomSanitizer,
                useValue: {
                    bypassSecurityTrustHtml: v => v
                }
            }, HighlightPipe
            ]
        });
    });

    beforeEach(inject([HighlightPipe], p => {
        pipe = p;
      }));

    it('highlights search term in the text', () => {
      let result = pipe.transform('search text', 'text');
        expect(result).toBe('search <span class="search-highlight">text</span>')
    });

    it('shoudl return same text', () => {
        let result = pipe.transform('search text', '');
        expect(result).toBe('search text', 'search text')
      });
});

@rex-ya

rex-ya commented Mar 4, 2018

Copy link
Copy Markdown

Perfect! Thank you! 👍

@kkumar981

kkumar981 commented Apr 13, 2018

Copy link
Copy Markdown

Hi Its working properly.

what ever the user enters in the input-field getting displayed with highlighted.

How to highlight the search text on plain html page. I tried with below code

My html page:-
<input type="text" [(ngModel)]="search">
<div [innerHTML]="data | highlight: search">

  </div> 

text1 xyz abc
text2 xyz abc
text3 xyz abc

---------------------------------------------------------------------------------------------------

highlight.component.ts

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

@pipe({ name: 'highlight' })
export class HighlightPipe implements PipeTransform {
transform(text: string, search, content): string {

console.log("search value here!! "+ search);
let pattern = search.replace(/[-[]/{}()*+?.\^$|]/g, '\$&');
pattern = pattern.split(' ').filter((t) => {
return t.length > 0;
}).join('|');
const regex = new RegExp(pattern, 'gi');
return search.replace(regex, (match) => <span class="highlight">${match}</span>);
}
}

@ParVisual

Copy link
Copy Markdown

Thanks!

@ma7moud-abdallah

Copy link
Copy Markdown

how can i do it without splitting the text to two parts, i need the matched pattern get the style in it's same position of the original text

@ankitgrover

Copy link
Copy Markdown

Below is the implementation which enables you to do a little more things:

  1. Highlight single match, global match, single match which starts with search string.
  2. Make the matching case sensitive.
  3. Pass a highlighting style class.

Implementation:

import {Pipe, PipeTransform} from "@angular/core";
import {SafeHtml} from "@angular/platform-browser";

@pipe({ name: "highlightText" })
export class HighlightPipe implements PipeTransform {

/* use this for single match search */
static SINGLE_MATCH:string = "Single-Match";
/* use this for single match search with a restriction that target should start with search string */
static SINGLE_AND_STARTS_WITH_MATCH:string = "Single-And-StartsWith-Match";
/* use this for global search */
static MULTI_MATCH:string = "Multi-Match";

constructor() {
}
transform(data: string,
          highlightText: string,
          option:string = "Single-And-StartsWith-Match",
          caseSensitive:boolean = false,
          highlightStyleName:string = "search-highlight"): SafeHtml {
    if (highlightText && data && option) {
        let regex:any = "";
        let caseFlag:string = !caseSensitive ? "i" : "";
        switch (option) {
            case "Single-Match": {
                regex = new RegExp(highlightText, caseFlag);
                break;
            }
            case "Single-And-StartsWith-Match": {
                regex = new RegExp("^" + highlightText, caseFlag);
                break;
            }
            case "Multi-Match": {
                regex = new RegExp(highlightText, "g" + caseFlag);
                break;
            }
            default: {
                // default will be a global case-insensitive match
                regex = new RegExp(highlightText, "gi");
            }
        }
        return data.replace(regex, (match) => `<span class="${highlightStyleName}">${match}</span>`);

    } else {
        return data;
    }
}

}

@calebeaires

Copy link
Copy Markdown

Great, but how can I add an event click to this highlighted text after it is rendered into innerhtml?

@wuenping

Copy link
Copy Markdown

this regex pattern mean what? thanks

@andreevsm

Copy link
Copy Markdown

This code is not quite correct. For example:

input - xaxa
users: [ 'xaxalololo', 'nonoxaxaxaxa', 'wtfxaxaxaxaxaxa' ]

Should works only to first occurrence, the code below is correct:

const regex = new RegExp(`${pattern}(.*?)`, 'i');

After that:
users: [ 'xaxalololo', 'nonoxaxaxaxa', 'wtfxaxaxaxaxaxa' ]

@rafcontreras

rafcontreras commented May 15, 2019

Copy link
Copy Markdown

In case someone is having problems with @ankitgrover 's code (Thank you!) :

import { Pipe, PipeTransform } from "@angular/core";
import { SafeHtml } from "@angular/platform-browser";

@Pipe({ name: "highlightText" })
export class HighlightPipe implements PipeTransform {
    /* use this for single match search */
    static SINGLE_MATCH: string = "Single-Match";
    /* use this for single match search with a restriction that target should start with search string */
    static SINGLE_AND_STARTS_WITH_MATCH: string = "Single-And-StartsWith-Match";
    /* use this for global search */
    static MULTI_MATCH: string = "Multi-Match";

    constructor() {}
    transform(
        contentString: string = null,
        stringToHighlight: string = null,
        option: string = "Single-And-StartsWith-Match",
        caseSensitive: boolean = false,
        highlightStyleName: string = "search-highlight"
    ): SafeHtml {
        if (stringToHighlight && contentString && option) {
            let regex: any = "";
            let caseFlag: string = !caseSensitive ? "i" : "";
            switch (option) {
                case "Single-Match": {
                    regex = new RegExp(stringToHighlight, caseFlag);
                    break;
                }
                case "Single-And-StartsWith-Match": {
                    regex = new RegExp("^" + stringToHighlight, caseFlag);
                    break;
                }
                case "Multi-Match": {
                    regex = new RegExp(stringToHighlight, "g" + caseFlag);
                    break;
                }
                default: {
                    // default will be a global case-insensitive match
                    regex = new RegExp(stringToHighlight, "gi");
                }
            }
            const replaced = contentString.replace(
                regex,
                (match) => `<span class="${highlightStyleName}">${match}</span>`
            );
            return replaced;
        } else {
            return contentString;
        }
    }
}

@rafcontreras

rafcontreras commented May 15, 2019

Copy link
Copy Markdown

Usage:

<div [innerHTML]="
  string
    | highlightText
      : string
      : 'string'
      : boolean
      : 'string'
"></div>
<td>
  <div [innerHTML]="
    row.content
    | highlightText
      : searchInputValue
      : 'MULTI_MATCH'
      : true
      : 'your-class'
  "></div>
</td>

@Superkarl

Copy link
Copy Markdown

@rafcontreras use this, so you can search for special characters too:
stringToHighlight = stringToHighlight.replace(/([-[\]{}()*+?.\\^$|#,])/g,'\\$1');

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment