A messenger displays the search results based on the input given by the user. Need to highlight the word that is been searched, while displaying the result. These are the h
If you have multiple words in your string than use pipe which accepts array and highlight each word in result.
You can use following pipe for multiple search words:-
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'highlight'
})
export class HighlightText implements PipeTransform {
transform(value: any, args: any): any {
if (!args) {return value;}
for(const text of args) {
var reText = new RegExp(text, 'gi');
value = value.replace(reText, "<mark>" + text + "</mark>");
//for your custom css
// value = value.replace(reText, "<span class='highlight-search-text'>" + text + "</span>");
}
return value;
}
}
Split you string to generate array of strings.
var searchTerms = searchKey.split(' ');
usage:
<div [innerHTML]="result | highlight:searchTerms"></div>
If you wanted to use custom class :
.highlight-search-text {
color: black;
font-weight: 600;
}
All the best!
Building on a previous answer (HighlightedText-Component) I ended up with this:
import { Component, Input, OnChanges } from "@angular/core";
@Component({
selector: 'highlighted-text',
template: `
<ng-container *ngFor="let match of result">
<mark *ngIf="(match === needle); else nomatch">{{match}}</mark>
<ng-template #nomatch>{{match}}</ng-template>
</ng-container>
`,
})
export class HighlightedTextComponent implements OnChanges {
@Input() needle: string;
@Input() haystack: string;
public result: string[];
ngOnChanges() {
const regEx = new RegExp('(' + this.needle + ')', 'i');
this.result = this.haystack.split(regEx);
}
}
This way also multiple matches of the needle are highlighted. The usage of this component is similar to the one in the previous answer:
<highlighted-text [needle]="searchInput" [haystack]="value"></highlighted-text>
For me this approach using a component feels more secure, since I do not have to use "innerHtml".
One difficulty the innerHTML method has is in styling the <mark>
tag. Another method is to place this in a component, allowing for much more options in styling.
highlighted-text.component.html
<mark *ngIf="matched">{{matched}}</mark>{{unmatched}}
highlighted-text.component.ts
import { Component, Input, OnChanges, OnInit } from "@angular/core";
@Component({
selector: "highlighted-text",
templateUrl: "./highlighted-text.component.html",
styleUrls: ["./highlighted-text.component.css"]
})
export class HighlightedTextComponent implements OnChanges {
@Input() needle: String;
@Input() haystack: String;
public matched;
public unmatched;
ngOnChanges(changes) {
this.match();
}
match() {
this.matched = undefined;
this.unmatched = this.haystack;
if (this.needle && this.haystack) {
const needle = String(this.needle);
const haystack = String(this.haystack);
const startIndex = haystack.toLowerCase().indexOf(needle.toLowerCase());
if (startIndex !== -1) {
const endLength = needle.length;
this.matched = haystack.substr(startIndex, endLength);
this.unmatched = haystack.substr(needle.length);
}
}
}
}
highlighted-text.component.css
mark {
display: inline;
margin: 0;
padding: 0;
font-weight: 600;
}
Usage
<highlighted-text [needle]=searchInput [haystack]=value></highlighted-text>
You can do that by creating a pipe and applying that pipe to the summary part of array inside ngfor
. Here is the code for Pipe
:
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'highlight'
})
export class HighlightSearch implements PipeTransform {
transform(value: any, args: any): any {
if (!args) {return value;}
var re = new RegExp(args, 'gi'); //'gi' for case insensitive and can use 'g' if you want the search to be case sensitive.
return value.replace(re, "<mark>$&</mark>");
}
}
and then in markup apply it on a string like this:
<div innerHTML="{{ str | highlight : 'search'}}"></div>
Replace 'search' with the word you want to highlight.
Hope this will help.
To expand on Kamal's answer,
The value coming into the transform method, could be a number, perhaps a cast to string String(value)
would be safe thing to do.
transform(value: string, args: string): any {
if (args && value) {
value = String(value); // make sure its a string
let startIndex = value.toLowerCase().indexOf(args.toLowerCase());
if (startIndex != -1) {
let endLength = args.length;
let matchingString = value.substr(startIndex, endLength);
return value.replace(matchingString, "<mark>" + matchingString + "</mark>");
}
}
return value;
}
The selected answer has the following issues:
i would suggest the following code instead
transform(value: string, args: string): any {
if (args && value) {
let startIndex = value.toLowerCase().indexOf(args.toLowerCase());
if (startIndex != -1) {
let endLength = args.length;
let matchingString = value.substr(startIndex, endLength);
return value.replace(matchingString, "<mark>" + matchingString + "</mark>");
}
}
return value;
}