0

Hi i am using Angular 5 for my web application. My use case is to return some dynamically created html contents from Java service and same to be used in a angular component. I am able to bypass angular security by DomSanitizer, but still the click event is not working.

Below is my code.

import { Pipe, PipeTransform } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
import DOMPurify from 'dompurify';
declare var angular: any;

@Pipe({
    name: 'safeHtml'
})
export class SafeHtmlPipe implements PipeTransform {

    constructor(protected sanitizer: DomSanitizer) {}

    public transform(value: any): any {
        const sanitizedContent = DOMPurify.sanitize(value);
        console.log(sanitizedContent);
        return this.sanitizer.bypassSecurityTrustHtml(sanitizedContent);

    }
}

This is thee angular safeHtml pipe used to by pass non trusted html.

Component.ts - for service call

import { Component, OnInit } from '@angular/core';
import {ViewReviewService} from '../../services/view-review-service';
import {Response} from '@angular/http';

@Component({
  selector: 'app-sample-hit',
  templateUrl: './sample-hit.component.html',
  styleUrls: ['./sample-hit.component.css']
})
export class SampleHitComponent implements OnInit {
  sampleData: any;
  
  constructor(private viewReviewService: ViewReviewService) { }

  ngOnInit() {
    
    this.hitSample();
  }

  hitSample() {
    this.viewReviewService.hitSample().subscribe((res: Response) =>  {
      console.log(res['_body']);
      this.sampleData =  res['_body'];
    });
  }

  
  callSampleFunction(value: string) {
    console.log('hit hit ' + value);
  }
}

This.sampleData = '<a> This is my a tag</a>
                  <a (click)="callSampleFunction(value1)"> This tag will call function</a>
                  <a (click)="callSampleFunction(value2)"> This tag will call same function with 
                  different value</a>
                  <p> other html </a>'

component.html

<div [innerHTML]="sampleData | safeHtml" >
</div>

What else i need to do. Please help ?

A T
  • 19
  • 6

2 Answers2

0

Injected html will not be "angularized" thus bindings like (click) will not work.

You must use pure javascript for that like "onClick"="someJsFunction". Then I don't know how to invoke component code from JS code (you must be able to do that eg from browser console).

One nasty workaround would be to expose component instance by setting global var.

Antoniossss
  • 31,590
  • 6
  • 57
  • 99
  • Following your suggestion. I created a Javascript file in the same sample component. Added function callSampleFunction() { alert('i am a javascript function'); } But now when i am clicking from browser i am facing function is not undefined. Any suggestion pls. – A T Oct 27 '20 at 15:13
  • And did you include your js file somewhere? As I wrote, you must be able to invoke that function in global scope (or any other defined scope) so it must work from browser console as well. Does it work if you call callSampleFunction from browser console? I guess not. JS files must either imported trough DOM or packaged into your application via scripts section in angular.js – Antoniossss Oct 27 '20 at 15:14
  • Yes I am not able to call that from browser. – A T Oct 27 '20 at 15:27
  • I included the JS file in index.html - Now this JS file should have global scope ? – A T Oct 27 '20 at 15:34
  • this path is simply invalid for ng serve. Please add that script to scripts section of angular.json https://stackoverflow.com/a/44817439/1527544 – Antoniossss Oct 27 '20 at 16:42
0

You can't make clickable elements by adding (click) since it's an angular event that won't compile inside an [innerHTML]. However there is a simple way similar to yours as follows:

1- add a unique class for your HTML element from backend or in the pipe as below:

public transform(value: any): any {
    const sanitizedContent = DOMPurify.sanitize(value);
    console.log(sanitizedContent);
    return this.sanitizer.bypassSecurityTrustHtml(this.modify(sanitizedContent));

}

modify(text: string){
 // add class here to your text and return it

}

Assuming your class name is someClass

This.sampleData = '<a class="someClass"> This is my a tag</a>
                  <a (click)="callSampleFunction(value1)"> This tag will call function</a>
                  <a (click)="callSampleFunction(value2)"> This tag will call same function with 
                  different value</a>
                  <p> other html </a>'

2-in your component.ts file after the view initialized event you can add the following code:

  ngAfterViewInit() {
    // assume dynamic HTML was added before
    const elementList = this.elRef.nativeElement.querySelectorAll('.someClass');
    if (elementList) {
        for (const element of elementList) {
        element.addEventListener('click', this.callSampleFunction.bind(this));
         // bind an event listener to every element with the class someClass
      }

    } 
  }
yazan
  • 518
  • 3
  • 16
  • That is not feasible. Because there will lakhs of element in worst case. The problem I am trying to solve is computation at java and create a HTML instead of Javascript. – A T Oct 27 '20 at 15:00
  • There have been pipes that triggers for an entire documents such as linkify pipe that i once used for an entire document and it was feasible. Check https://stackblitz.com/edit/linkify-pipe – yazan Oct 27 '20 at 15:11