0

I am currently facing an issue while trying to parse and inject HTML into my application.

  1. I get a string from an API
  2. String is parsed, and some content is replaced by a tag at the service level. The new string looks like this:

    [mention]{\"label\": \"Stack\"}[/mention] with some other text
    and a second [mention]{\"label\": \"Overflow\"}[/mention] with text
    
  3. Then the string is displayed by a component in the view

    <div class="custom">{{ parsedString }}</div>
    

I tried multiple methods like using innerHTML with a sanitizeHTML pipe, and read some posts of people having this issue when adding router links to their HTML, but I seem to not understand properly how to use viewContainerRef in my case.

I saw this answer linked multiple times, but to me it's a mashed potato of code and I cannot get the logic behind it...


Update

So I ended up using a Pipe to parse the string and have a template using each part of the string to display a component if needed.

  1. My view component gets the string from an API
  2. The view component inserts the string in its template and use a pipe to parse it.
  3. The filter returns an array of strings which can be used in an *ngFor loop

view.component.ts

<div *ngFor="let string of text | mentions">
    <ng-container *ngIf="string.label">
        <mention> <!-- We can use a component with dynamic HTML ! -->
            {{ string.label }}
        </mention>
    </ng-container>

    <ng-container *ngIf="!string.label">
        {{ string }}
    </ng-container>
</div>

mentions.pipe.ts

/* Angular */
import { Injectable, Pipe, PipeTransform } from '@angular/core';


@Pipe({
    name: 'mentions'
})

@Injectable()
export class MentionsPipe implements PipeTransform {
    tryParseJSON(string: string) {
        try {
            const json = JSON.parse(string);

            if (json && typeof json === 'object') {
                return json;
            }
        } catch (e) {}

        return string;
    };

    transform(value: string) {
        const regex = /\[mention\]([^[]*(?:\[(?!mention\]|\/mention\])[^[]*)*)\[\/mention\]/ig;

        return value
            .split(regex)
            .map((match: string) => {
                // The mention is a stringified JSON, so we want to parse it
                return this.tryParseJSON(match);
            });
    }
}

This may not be the best solution as it would mean using the pipe in an *ngFor loop each time we know that dynamic HTML will include mentions (in my case), but for now I am ok with this, until Angular provides a nice and secured way to detect and replace components in injected HTML.

Niflhel
  • 663
  • 1
  • 5
  • 19

1 Answers1

0

Angular blocks run time compile. So you need to do some tricks. https://github.com/patrikx3/angular-compile => works on Angular 4. Not 5, so it has no future.

I've solved this problem using dynamic components. I can recommend https://medium.com/front-end-hacking/dynamically-add-components-to-the-dom-with-angular-71b0cb535286 this guide.

Ulfsark
  • 902
  • 1
  • 11
  • 26