Angular, how to create a highlighter that conditionally styles the text


Angular highlight for search text

In this example we want to highlight some text inside a page or inside a table.

angular highlighter

In the image you can see that the text searched ‘einzelbillet’ is dynamically highlighted in the list of answers to better visually find the relevant information.

This filter is applied on thousand of results and reduces the time required for the user to 'scan' the list of results.

In our tutorial we will build something much simpler:

angular highlighter example

How it works

The idea is to create a pipe that searches in each result the text used for the filter. If the result is found the original text is modified adding a <span class=“highlight”> tag before the searched text.

import { Pipe, PipeTransform } from '@angular/core'; 
import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; 
 
/* 
In the decorator we define the name of the Pipe 
*/ 
@Pipe({ 
  name: 'highlighter', 
}) 
export class HighlighterPipe implements PipeTransform { 
  // a Pipe must implement PipTransform ... not a lot of choice here 
 
  /* 
   text: the text that contains the string to be searched for 
   search: the text to be found 
   cssClass: we created a styling class to highlight the text, this can be overriden 
  */ 
  transform( 
    originalText: string, 
    textToFind, 
    cssClass: string = 'highlighter' 
  ): string { 
    // check the parameters, if something is missing we simply return the original text 
 
    if (typeof originalText !== 'string' || !textToFind) { 
      return originalText; 
    } 
 
    const pattern = textToFind 
      .replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&') 
      .split(' ') 
      .filter((t) => t.length > 0) 
      .join('|'); 
 
    const regex = new RegExp(pattern, 'gi'); 
 
    let result = textToFind 
      ? originalText.replace( 
          regex, 
          (match) => `<span class=${cssClass}">${match}</span>` 
        ) 
      : originalText; 
 
    return result; // no need to sanitize 
  } 
} 

Transform method

Our transform method (mandatory implementing PipeTransform) receives 3 parameters

transform(originalText: string,textToFind,cssClass: string = 'highlighter'): string { 

originalText is the text that has to analyzed and could contain the string to find

textToFind is the string to be searched in the text

cssClass in our case we want to be able to customize the style of the string found. By default we create an highlighter CSS class

Pattern to use in Regex

In our case we want to be able to search multiple words. To do this we create a a pattern to use in a regular expression

const pattern = textToFind 
  .replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&') 
  .split(' ') 
  .filter((t) => t.length > 0) 
  .join('|'); 

For example, if the textToFind is Angular Rocks the resulting patter will be Angular|Rocks.

Regex

const regex = new RegExp(pattern, 'gi'); 

The regex searches the pattern previously defined. The parameter ’gi’ means that the pattern has to be searched g globally and can be i insensitive to the case.

In our example Angular 13 rocks will be found and valid.

Return the result

let result = textToFind 
  ? originalText.replace( 
    regex, 
    (match) => `<span class=${cssClass}">${match}</span>` 
  ) 
  : originalText; 
 
return result; // no need to sanitize 

In the original text, we will search the regex previously built. If there is a match the text will surround the match with a <span> tag that adds a CSS class and return the result as a simple string .

If no match is found then the original text is sent as result.

For information, we are returning some text with HTML, because our code is basic HTML we don’t need to sanitize the DOM. If you want to use style to modify the color of the text avoiding the external CSS, the result has to be sanitized.

Usage in HTML

Here you have an example of usage of the Pipe

<input type="text" [(ngModel)]="filterText" /> 
<br /> 
<p 
  [innerHtml]=" 
    textToShowAsResult | highlighter: filterText:getHighlighterClass() 
  " 
></p> 

Because we could add some html tag to the original text we need to show the result in an element with the [innerHtml] property.

In this example, we call our highlighter pipe with multiple parameters: | highlighter: filterText:getHighlighterClass() .

filterText is the text contained in the ngModel bind to the input text , this is the text that will be searched in the textToShowAsResult variable.

getHighlighterClass() to complicate a bit our example we use a method to dynamically retrieve the CSS class name to be used to color the text.

CSS Style

We use only one class in our example. Very basic.

.highlighter { 
  color: red; 
  font-size: 1em; 
  text-decoration: underline; 
} 

Component

@Component({ 
  selector: 'my-app', 
  templateUrl: './app.component.html', 
  styleUrls: ['./app.component.css'], 
  encapsulation: ViewEncapsulation.None, 
}) 
export class AppComponent { 
  public filterText: string = 'Angular rocks'; 
  textToShowAsResult = 'Text to be highlighted: Angular 13 Rocks!!!'; 
   
  getHighlighterClass(): string { 
    return "'highlighter'"; 
  } 
} 

Our component is pretty straightforward.

What it is important to note is that we use ViewEncapsulation.None , this allows the defined style to be used in every element present on the page. With the default ViewEncapsulation.Emulated the component cannot access the styles with innerHtml .

Stackblitz

You can test and play with this implementation published on Stackblitz


You could be interested in

Right click context menu with Angular

Right click custom menu inside dynamic lists with Angular Material
2020-05-31

Enums in Angular templates

How to use enum in the html template
2019-01-21

WebApp built by Marco using SpringBoot, Java 17, Mustache, Markdown and in Azure