4

I am working on an generate dynamic template using angular 6. I have an API that return strings like below:

    <button type="button" (click)="openAlert()">click me</button>

and html

    <div [innerHtml]="myTemplate | safeHtml">
      </div>

function is bellow:

    openAlert() {
        alert('hello');
      }
4
  • There should be some error in your browser console. Commented Oct 31, 2019 at 10:45
  • no error console is clear Commented Oct 31, 2019 at 10:47
  • Can you provide stackblitz example> Commented Oct 31, 2019 at 10:49
  • This will not work, because method contains in string will be treated as text Commented Oct 31, 2019 at 10:50

3 Answers 3

10

You cannot bind angular events directly to innerHTML.

Still if you need to attach the event listeners you need to to do it after the html content is loaded.

Once the content is set to the variable, ngAfterViewInit Angular life cycle event will be triggered. Here you need to attach the required event listeners.

Checkout the working example below.

component.html

<button (click)="addTemplate()">Get Template</button>
<div [innerHTML]="myTemplate | safeHtml"></div>

component.ts

import { Component, ElementRef } from '@angular/core';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  name = 'Angular';
  myTemplate  = '';

  constructor(private elementRef:ElementRef){

  }
  openAlert() {
    alert('hello');
  }
  addTemplate(){
     this.myTemplate  = '<button type="button" id="my-button" (click)="openAlert()">click mee</buttn>';
  }
  ngAfterViewChecked (){
    if(this.elementRef.nativeElement.querySelector('#my-button')){
      this.elementRef.nativeElement.querySelector('#my-button').addEventListener('click', this.openAlert.bind(this));
    }
  } 
}

safe-html.pipe.ts

import { Pipe, PipeTransform } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
@Pipe({
  name: 'safeHtml'
})
export class SafeHtmlPipe implements PipeTransform {
constructor(private sanitized: DomSanitizer) {}
  transform(value) {
        return this.sanitized.bypassSecurityTrustHtml(value);
    }

}
3
  • 2
    Putting it in afterViewChecked will potentially add the same handler multiple times. Commented May 11, 2020 at 19:15
  • Edited the original answer to replace ngAfterViewChecked with ngAfterViewInit. Commented Jul 26, 2021 at 23:05
  • @Farzin Zaker ngAfterViewInit is invoked only once when the view is instantiated. But ngAfterViewChecked is called after the default change detector has completed checking a component's view for changes. This is useful when you set data after component is loaded. Example after api call response. Commented Jul 26, 2021 at 23:21
2

this should work too:

component.html

<div #template></div>

component.ts

@ViewChild('template') myTemplate: ElementRef;

addTemplate(){
   this.myTemplate.nativeElement.innerHTML = '<button type="button">click me</button>';
   this.myTemplate.nativeElement.addEventListener('click', this.openAlert);
}
0

Basically this will not work. When you write code in Angular, it is transpiled by webpack and converted to javascript that is executed in the browser.

However, now you are injecting Angular code dynamically and not building it. The event detection (click) would not work natively and the function openAlert is also not in the global scope where it is injected. You will have to consider a different approach and generate content using <ng-template> based on response from the API.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.