4
votes

I am pretty new to Angular 2. I am trying to achieve the same task as Highlight the search text - angular 2 what is mentioned in above post. I have created the pipe filter my question is where should i keep the pipe filter and where should i place the inner html div.

Copying the problem:

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 html and component that is been used.

component.html

<div *ngFor = "let result of resultArray">
<div>Id : result.id </div>
<div>Summary : result.summary </div>
<div> Link : result.link </div>
</div>

Component.ts

resultArray : any = [{"id":"1","summary":"These are the results for the searched text","link":"http://www.example.com"}]

This resultArray is fetched from hitting the backend service by sending the search text as input. Based on the search text, the result is fetched. Need to highlight the searched text, similar to google search.

How should i apply the search filter and where should i keep the inner html?

2
have you tried fetching the substring and marking it with the HTML TAG needed to highlight the text? - GustavoAdolfo

2 Answers

15
votes

There are some tweaks to be made to the regex replacement regarding case, but here's a starting point:

//our root app component
import {Component, NgModule, VERSION, Pipe, PipeTransform} from '@angular/core'
import {BrowserModule, DomSanitizer} from '@angular/platform-browser'

@Pipe({
    name: 'highlight'
})
export class HighlightSearch implements PipeTransform {
  constructor(private sanitizer: DomSanitizer){}

  transform(value: any, args: any): any {
    if (!args) {
      return value;
    }
    // Match in a case insensitive maneer
    const re = new RegExp(args, 'gi');
    const match = value.match(re);

    // If there's no match, just return the original value.
    if (!match) {
      return value;
    }

    const result = value.replace(re, "<mark>" + match[0] + "</mark>");
    return this.sanitizer.bypassSecurityTrustHtml(result);
  }
}

@Component({ 
  selector: 'my-app',
  template: `
    <input (input)="updateSearch($event)">
    <div *ngFor="let result of resultArray" [innerHTML]="result.summary | highlight: searchTerm"></div>
  `,
})
export class App {
  results: string[]
  searchTerm: string;
  constructor() {
    this.resultArray = [
      {
        "id": "1",
        "summary": "These are the results for the searched text",
        "link": "http://www.example.com"
      },
      {
        "id": "2",
        "summary": "Here are some more results you searched for",
        "link": "http://www.example.com"
      }
    ]
  }
  updateSearch(e) {
    this.searchTerm = e.target.value
  }
}

@NgModule({
  imports: [ BrowserModule ],
  declarations: [ App, HighlightSearch ],
  bootstrap: [ App ]
})
export class AppModule {}

Plnkr

Edit: Plnkr seems unhappy. StackBlitz

0
votes

you can easily use this directive

usage:

<label [jsonFilter]="search">{{text}}</label>

directive

import {
      AfterViewInit,
      Directive,
      ElementRef,
      Input,
      OnChanges,
      SimpleChanges
    } from '@angular/core';
    
    @Directive({
      selector: '[jsonFilter]'
    })
    export class FilterDirective implements OnChanges, AfterViewInit {
    
      @Input() jsonFilter = '';
      constructor(
        private el: ElementRef,
      ) {
      }
    
      ngOnChanges(changes: SimpleChanges) {
        this.ngAfterViewInit();
      }
    
      ngAfterViewInit() {
        const value = this.el.nativeElement?.innerText.split('\n').join('');
        if (!value) return;
        const re = new RegExp(this.jsonFilter, 'gi');
        const match = value.match(re);
        if (!match || match.some(x => !x)) {
          this.el.nativeElement.innerText = value;
        } else {
          this.el.nativeElement.innerHTML =  value.replace(re, "<mark>" + match[0] + "</mark>")
        }
    
      }
    
    }