Home > Mobile >  Click eventListener binding not working on dynamic HTML Angular 12
Click eventListener binding not working on dynamic HTML Angular 12

Time:08-20

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.

  • Related