i have an issue while click binding on dynamic html.I tried setTimeout
function but click event not binding on button.i have also tried template referance on button and get value with @ViewChildren
but @ViewChildren
showing null value.
Typscript
export class AddSectionComponent implements OnInit {
sectionList: any = [];
constructor(private elRef: ElementRef,private _httpService: CommonService ,private sanitized: DomSanitizer) { }
ngOnInit(): void {
this.getSectionList();
}
ngAfterViewInit() {
let element = this.elRef.nativeElement.querySelector('button');
if (element) {
element.addEventListener('click', this.bindMethod.bind(this));
}
}
bindMethod() {
console.log('clicked');
}
sanitizeHtml(value: string): SafeHtml {
return this.sanitized.bypassSecurityTrustHtml(value)
}
getSectionList() {
//API request
this._httpService.get('/Section/GetSectionList').subscribe(res => {
if (res) {
this.sectionList = res.json();
//sectionList is returning below HTML
//<div >
// <button type='button' class='btn btn-primary btn-sm'>Click Me</button>
//</div>
}
})
}
}
Template
<ng-container *ngFor="let item of sectionList">
<div [innerHTML]="sanitizeHtml(item?.sectionBody)">
</div>
//innerHTML after rendering showing this
//<div >
// <button type='button' class='btn btn-primary btn-sm'>Click Me</button>
//</div>
</ng-container>
CodePudding user response:
Short Answer, you are binding functions inside your templates, which means you have a new html content every time change detection runs, and change detection runs everytime a function is called, which means your button keeps on being updated infinitely, that's why it never works, Read more here please.
Now on how to do this, I would listen to ngDoCheck, and check if my button has a listener, if not, I will append the listener. I will also make sure to use on Push change detection, because if not, this will ngDoCheck will be called a lot, and maybe the button will be replaced more often, not quite sure about it. Here is how the code would look like.
html
<!-- no more binding to a function directly -->
<div #test [innerHTML]='sanitizedHtml'></div>
component
import { HttpClient } from '@angular/common/http';
import { AfterViewChecked, AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, DoCheck, ElementRef, OnDestroy, ViewChild } from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class AppComponent implements DoCheck {
name = 'Angular';
people: any;
//now we are getting the div itself, notice the #test in the html part
@ViewChild('test')
html!: ElementRef<HTMLDivElement>;
//a property to hold the html content
sanitizedHtml!: SafeHtml;
constructor(private _http: HttpClient, private sanitized: DomSanitizer,private change: ChangeDetectorRef ) {}
ngDoCheck(): void {
//run with every change detection, check if the div content now has a button and attach the click event
if (this.html != undefined) {
let btn = this.html.nativeElement.querySelector('button');
if (btn && btn.onclick == undefined) {
btn.onclick = this.bindMethod.bind(this);
}
}
}
ngOnInit() {
this.peoples();
}
peoples() {
this._http.get('https://swapi.dev/api/people/1').subscribe((item: any) => {
const people = `<div >
<p>${item['name']}</p>
<button type='button' class='btn btn-primary btn-sm'>Click Me</button>
</div>`;
//assign the html content and notify change detection
this.sanitizedHtml = this.sanitized.bypassSecurityTrustHtml(people);
this.change.markForCheck();
});
}
bindMethod() {
console.log('clicked');
}
}
I don't like the approach because of the need to listen to ngDoCheck, this can run a lot, especially if you don't use onpush change detection. I hope this helped.