Arron
Arron

Reputation: 1164

how to highlight a word in a paragraph in angular 2?

I have done this in angular 1.x but i want to know how to highlight a dynamic word in angular 2..in angular 1.x iam using a keyword

        <td><div highlight="var" keywords="somename">      {{paragraph}}</div></td>

I have done the above html by using the below angular-highlight.js .

     angular.module('angular-highlight', [])
      .directive('highlight', function()
    {

    var component = function(scope, element, attrs) {

    if (!attrs.highlightClass) {
        attrs.highlightClass = 'angular-highlight';
    }

    var replacer = function(match, item) {
        return '<span class="'+attrs.highlightClass+'">'+match+'</span>';
    }
    var tokenize = function(keywords) {
        keywords = keywords.replace(new RegExp(',$','g'), '').split(',');
        var i;
        var l = keywords.length;
        for (i=0;i<l;i++) {
            keywords[i] = '\\b'+keywords[i].replace(new RegExp('^ | $','g'), '')+'\\b';
        }
        return keywords;
    }

    scope.$watch('keywords', function() {
        //console.log("scope.keywords",scope.keywords);
        if (!scope.keywords || scope.keywords == '') {
            element.html(scope.highlight);
            return false;
        }


        var tokenized   = tokenize(scope.keywords);
        var regex       = new RegExp(tokenized.join('|'), 'gmi');

        //console.log("regex",regex);

        // Find the words
        var html = scope.highlight.replace(regex, replacer);

        element.html(html);
    });
}
return {
    link:           component,
    replace:        false,
    scope:          {
        highlight:  '=',
        keywords:   '='
    }
};
});

Upvotes: 8

Views: 8206

Answers (2)

maxime1992
maxime1992

Reputation: 23803

In case someone is interested in a simple (generic) solution, I came up with a directive (based on Thierry Templier work !).

This directive allows you to pass the text to work with, the search text and a class to apply :

import { Directive, ElementRef, Renderer, Input, OnInit } from '@angular/core';
import { escapeStringRegexp } from '../helpers/helper';

@Directive({
  selector: '[appColorSearchedLetters]'
})
export class ColorSearchedLettersDirective implements OnInit {
  @Input() search: string;
  @Input() text: string;
  @Input() classToApply: string;

  constructor(private el: ElementRef, private renderer: Renderer) { }

  ngOnInit() {
    if (typeof this.classToApply === 'undefined') {
      this.classToApply = '';
    }

    if (typeof this.search === 'undefined') {
      this.renderer.setElementProperty(this.el.nativeElement, 'innerHTML', this.text);
      return;
    }

    let search = escapeStringRegexp(this.search.toString());
    this.renderer.setElementProperty(this.el.nativeElement, 'innerHTML', this.replace(this.text, search));
  }

  replace(txt: string, search: string) {
    let searchRgx = new RegExp('('+search+')', 'gi');

    return txt.replace(searchRgx, `<span class="${this.classToApply}">$1</span>`);
  }
}

And the helper

import { escapeStringRegexp } from '../helpers/helper';

contains :

let matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g;

export function escapeStringRegexp (str) {
  if (typeof str !== 'string') {
    throw new TypeError('Expected a string');
  }

  return str.replace(matchOperatorsRe, '\\$&');
};

This function is from https://www.npmjs.com/package/escape-string-regexp and credit goes to Sindresorhus.

Here's how I use it :

<span appColorSearchedLetters [search]="search" [text]="user.name" classToApply="searched"></span>

Upvotes: 6

Thierry Templier
Thierry Templier

Reputation: 202176

I would create a custom directive for this:

@Directive({
  selector: '[highlight]'
})
export class HighlightDirective {
  @Input()
  keywords:string;

  highlightClass: string = 'highlight';

  constructor(private elementRef:ElementRef,private renderer:Renderer) {

  }

  replacer(match, item) {
    return `<span class="${this.highlightClass}">${match}</span>`;
  }

  tokenize(keywords) {
    keywords = keywords.replace(new RegExp(',$','g'), '').split(',');
    return keywords.map((keyword) => {
      return '\\b'+keyword.replace(new RegExp('^ | $','g'), '')+'\\b';
    });
  }  

  ngOnChanges() {
    if (this.keywords) {
      var tokenized = this.tokenize(this.keywords);
      var regex = new RegExp(tokenized.join('|'), 'gmi');

      var html = this.elementRef.nativeElement.innerHTML.replace(regex, (match, item) => {
        return this.replacer(match, item);
      });

      this.renderer.setElementProperty(this.elementRef.nativeElement, 'innerHTML', html);
    }
  }
}

And use it like this:

@Component({
  selector: 'app'
  template: `
    <p highlight keywords="test,angular2">
    this is a test to highlight words with angular2
    </p>
  `,
  styles: [`
    .highlight {
      background-color: yellow;
    }
  `]
  directives: [ HighlightDirective ]
})
export class App {
}

According to the ViewEncapsulation used, you could need an hack (in the emulated one - default on) to add an attribute to be able to see styles applied:

replacer(match, item) {
  return `<span ${encapsulationAttribute} class="${this.highlightClass}">${match}</span>`;
}

ngOnChanges() {
  this.initializeEncapsulationAttribute();
  (...)
}

initializeEncapsulationAttribute() {
  if (!this.encapsulationAttribute) {
    var attributes = this.elementRef.nativeElement.attributes;
    for (var i = 0; i<attributes.length; i++) {
      let attr = attributes[i];
      if (attr.name.indexOf('_ngcontent') != -1) {
        this.encapsulationAttribute = attr.name;
        break;
      }
    }
  }
}

See this plunkr: https://plnkr.co/edit/XxB1pFEyUHlZetxtKMUO?p=preview.

Upvotes: 8

Related Questions