4

I want to attach a click event to the dynamically created html element. The click event will should be able to fire another method in component.

I have already gone through other SO answers suggesting to use ElementRef to attach event. But, its not working for my case.

I am using mapQuest API to render map. The map will have geocode plotted & it will add a roll over content. To add the rollover content I am using the API method of mapQuest, like info.setInfoContentHTML('<a>click me</a>');

Just for reference: https://developer.mapquest.com/documentation/javascript-api/pois-infowindows/

The link will be inside the popup which will be added to the dom dynamically by plugin when user hover on the icon. How to add event listener on the link which is shown only when the user hover over & there is no callback event provided by plugin which can fire after hover popup is displayed.

I was looking for jQuery live like functionality which would attach event listener though the element is not on the DOM.

Jay
  • 408
  • 2
  • 6
  • 27
  • basically map gets ready. All addresses are geolocated on map. But the popup that comes on hover is added dynamically to the dom whenever user hover over any pin. My link is inside that popup. – Jay Jan 10 '19 at 13:32
  • sorry, I don't have live link right now. Are you adding dynamic html as a string? or to the document object? How are you assigning the event? – Jay Jan 10 '19 at 13:46
  • I create a HTML element and after `info.onclick = function(){}`. For instance `info` is my HTML element. Once Map is ready, the info exist but does not display, and binding event works. – Maihan Nijat Jan 10 '19 at 13:54
  • Would be helpful if you let me know how you create `info`. – Maihan Nijat Jan 10 '19 at 13:57
  • 'var info = new MQA.Poi({ lat: 39.743943, lng: -105.020089 });' Like this. This is a plugin method. – Jay Jan 10 '19 at 13:58

3 Answers3

10

Because Angular processes the template when the component is compiled. Any HTML added later is not compiled again and bindings are ignored.

You can use the following :

constructor(private elRef:ElementRef) {}

ngAfterViewInit() {
  // assume dynamic HTML was added before
  this.elRef.nativeElement.querySelector('button').addEventListener('click', 
  this.onClick.bind(this));
}

your use case :

public createElement(){
  
  const el = '<button>click me</button>';
  this.elRef.nativeElement.querySelector('button').addEventListener('click', 
  this.methodName.bind(this));
  info.setInfoContentHTML(el);
}

public methodName(){
    
      console.log('burrah!!! called');
    }

2022 update. :

Stackblitz demo

programoholic
  • 4,830
  • 5
  • 20
  • 59
  • I tried this. instead of button I used class name of the anchor link. It did not fire the event. – Jay Jan 10 '19 at 13:49
  • One thing here is my links will not be on DOM during ngAfterViewInit event processing. The link is added dynamically by plugin when user hover over the address pin. – Jay Jan 10 '19 at 13:50
  • @Jay Button is the element name not class.... also you can put that piece of code into a method and call after whenever the element is created. – programoholic Jan 10 '19 at 13:52
  • Element is created dynamically by plugin. The configuration of the plugin accepts the html string data that would be displayed when certain event happens. – Jay Jan 10 '19 at 13:57
  • yes, I am passing the string. to the plugin. Plugin creates the hover popup. – Jay Jan 10 '19 at 14:01
  • no, it didn't call. To reproduce my scenario. Add a button on your view. Onclick of that button add some button to div container using document.getElementById("#div").innerHTML. Rest of your code remains same. – Jay Jan 10 '19 at 14:18
  • share stackblitz if you need further help :) – programoholic Jan 10 '19 at 14:21
  • Yes. I will do. Give me some time. – Jay Jan 10 '19 at 14:23
  • For now I solved using idea from this link - https://stackoverflow.com/questions/35296704/angular2-how-to-call-component-function-from-outside-the-app – Jay Jan 10 '19 at 15:08
1

Better way, preserves "this" context automatically:

this.elementRef.nativeElement.querySelector('.dynamic-anchor').addEventListener('click', () => {
  this.myComponentFunction();
});
Chris Fremgen
  • 4,649
  • 1
  • 26
  • 26
0

I tried solution of @programoholic. it works but I need change little bit.
After doing this, there is two problems are,

  1. Fired event could be multiple.(Click "Create button" and check console log)
  2. If button added a lot, you need to check a index at elementList to add event listener.

Here is sample.

btnClicked(){

  //create botton
  let element = document.getElementById(this.uuid);    
  this.buttonHtml = `<button>BtnTest - ${this.btnIndex++}</button>`;
  element.innerHTML = element.innerHTML + this.buttonHtml;

  //after add button, get elment list by "querySelectorAll".
  let elementList = this.elRef.nativeElement.querySelectorAll('button');

  //Add event handler each.
  elementList[0].addEventListener('click', this.btnCellClick1.bind(this));
  elementList[1].addEventListener('click', this.btnCellClick2.bind(this));
}

btnCellClick1(){
     console.log('btn Click 1');
}

btnCellClick2(){
  console.log('btn Click 2');
}
jornathan
  • 646
  • 5
  • 13