如何创建管道以突出显示具有给定单词列表的句子中的文本

时间:2018-07-01 08:46:56

标签: html angular typescript

我有一个单词列表和一个句子列表。我想突出显示列表中句子中的文本。

例如:

句子:“宋南人”

words:[“ song”,“ nam”,“ p”]

我想在打字稿中创建一个管道,以产生类似。

的输出。

歌曲 姓名 p 人。

我尝试了以下解决方案:

import { PipeTransform, Pipe } from "@angular/core";

@Pipe({ name: "highlight" })
export class HighlightPipe implements PipeTransform {  

  transform(text: string, searchWords: string[]): string {

    if (searchWords.length && text) {
      searchWords.forEach((searchWord: string) => {
        text = text.replace(searchWord, (match) => `<span class="highlightText">${match}</span>`);
      });
    }
    return text;
  }
}

上述解决方案的问题:

  1. 它遍历单词列表。
  2. 它在句子中找到单词“ song”,并将其替换为<span>
<span class='highlightText'>song</span> nam person
  1. 然后,它找到单词“ nam”,并将其替换为<span>
<span class='highlightText'>song</span> <span class='highlightText'>nam</span> person
  1. 然后搜索“ p”,并用新跨度替换“跨度”内的“ p”。在显示时会产生问题。
<s <span class='highlightText'>p</span>an class='highlightText'>song</span> <span class='highlightText'>nam</span> <span class='highlightText'>person</span>

如果有人可以提供此问题的解决方案,将不胜感激。 谢谢

1 个答案:

答案 0 :(得分:5)

您最初的想法很好。接受输入文本并对其进行修改(例如输出),以给定的类包装文本。

使用Angular,当您要操作DOM时,有两点需要牢记:
 -使用指令(不是管道)
 -使用渲染器,永远不要自己操纵DOM

也就是说,这就是我构建指令的方式:

import { Directive, Input, SimpleChanges, Renderer2, ElementRef } from '@angular/core';

@Directive({
  selector: '[appHighlight]'
})
export class HighlightDirective {
  @Input() searchedWords: string[];
  @Input() text: string;
  @Input() classToApply: string;

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

  ngOnChanges(changes: SimpleChanges): void {
    if (!this.searchedWords || !this.searchedWords.length || !this.classToApply) {
      this.renderer.setProperty(this.el.nativeElement, 'innerHTML', this.text);
      return;
    }

    this.renderer.setProperty(
      this.el.nativeElement,
      'innerHTML',
      this.getFormattedText()
    );
  }

  getFormattedText() {
    const re = new RegExp(`(${ this.searchedWords.join('|') })`, 'g');

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

然后可以在HTML中调用:

<p
  appHighlight
  [searchedWords]="['song', 'name', 'p']"
  text="song name person"
  classToApply="selected"
></p>

当然,您也可以构建动态版本:

TS:

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  text = new FormControl('song name person')
  searchedWordsControl = new FormControl('')

  searchedWords$: Observable<string[]> = this
    .searchedWordsControl
    .valueChanges
    .pipe(
      map((search: string) => search.trim().split(' '))
    )
}

HTML:

Text:
<input type="text" placeholder="Text" [formControl]="text">

Searched words:
<input type="text" placeholder="Searched words" [formControl]="searchedWordsControl">

<p
  appHighlight
  [searchedWords]="searchedWords$ | async"
  [text]="text.value"
  classToApply="selected"
></p>

这是一个包含静态和动态版本的Stackblitz示例:
https://stackblitz.com/edit/angular-rdfyuq