Search code examples
angulartypescriptdata-binding

Angular: How do I use dynamic text for data binded input


I want to dynamically change the styling of select words in a sentence (with a custom pipe) like so:

<p>{{ product.sentence | italicArray:product.italicsArray }}</p>

This creates:

Hi this is a <span><i>test</i></span> sentence

How do I enforce the span and italics tags and not display them as clear text?


Solution

    • We can loop through the italics array

    • then we use regex new RegExp('\\b' + str + '\\b', 'g') to get all the whole words with the match string and then wrap it around <i><i/>

    • We can use DomSantizer to sanitize the transformed data.

    • Finally since we have HTML in the code now, we need to render it on innerHTML which will give us the desired output, but there is a catch, we cannot have angular events/binding since its rendering outside the angular context

    Answer from Blazzze IL helped this answer do Upvote that answer!

    Working example below

    pipe

    import { CommonModule } from '@angular/common';
    import { Pipe, PipeTransform, Sanitizer, SecurityContext } from '@angular/core';
    import { DomSanitizer } from '@angular/platform-browser';
    
    @Pipe({
      name: 'italicArray',
      standalone: true,
    })
    export class ItalicArrayPipe implements PipeTransform {
      constructor(private sanitizer: DomSanitizer) {}
    
      transform(value: string, strArray = []): any {
        let output: string = value;
        strArray.forEach((str: string) => {
          output = <string>this.sanitize(this.replace(output, str));
        });
        return output;
      }
    
      replace(value: string, str: string): string {
        var regex = new RegExp('\\b' + str + '\\b', 'g');
        str = value.replace(regex, `<i>${str}</i>`);
        return str;
      }
    
      sanitize(str: string) {
        console.log(this.sanitizer);
        return this.sanitizer?.sanitize(SecurityContext.HTML, str) || '';
      }
    }
    

    main.ts

    import { Component, importProvidersFrom } from '@angular/core';
    import { bootstrapApplication } from '@angular/platform-browser';
    import { CommonModule } from '@angular/common';
    import 'zone.js';
    import { ItalicArrayPipe } from './italic-array.pipe';
    
    @Component({
      selector: 'app-root',
      standalone: true,
      imports: [CommonModule, ItalicArrayPipe],
      template: `
        <p [innerHTML]="product.sentence | italicArray: product.italicsArray"></p>
      `,
    })
    export class App {
      name = 'Angular';
      product: any = {
        sentence:
          'Hi this is a test sentence, which is a working example, and is the final solution',
        italicsArray: ['test', 'is'],
      };
    }
    
    bootstrapApplication(App);
    

    stackblitz