-
-
Save adamrecsko/0f28f474eca63e0279455476cc11eca7 to your computer and use it in GitHub Desktop.
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> | |
* | |
*/ | |
Nice one, thanks! :)
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.
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 */ }
<span [innerHTML]="text | highlight: search">
works perfectly, thanks a lot.
Awesome! Thank you!
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;
}
}
}
This what I am looking for. Workers Perfectly fine.
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;
}
Thanks works fine
but what about : "WARNING: sanitizing HTML stripped some content (see http://g.co/ng/security#xss)."
@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?
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;
}
}
}
Thanks it works fine...
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')
});
});
Perfect! Thank you! 👍
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>
---------------------------------------------------------------------------------------------------
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>
);
}
}
Thanks!
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
Below is the implementation which enables you to do a little more things:
- Highlight single match, global match, single match which starts with search string.
- Make the matching case sensitive.
- 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;
}
}
}
Great, but how can I add an event click to this highlighted text after it is rendered into innerhtml?
this regex pattern mean what? thanks
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' ]
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;
}
}
}
Usage:
<div [innerHTML]="
string
| highlightText
: string
: 'string'
: boolean
: 'string'
"></div>
<td>
<div [innerHTML]="
row.content
| highlightText
: searchInputValue
: 'MULTI_MATCH'
: true
: 'your-class'
"></div>
</td>
@rafcontreras use this, so you can search for special characters too:
stringToHighlight = stringToHighlight.replace(/([-[\]{}()*+?.\\^$|#,])/g,'\\$1');
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.