Saeed sdns
Saeed sdns

Reputation: 980

listening to click on specific element in angular

I have a question about the angular concept. I ask it with an example.

For example, I have some links similar to these items in my off-canvas menu:

<ul class="my_ul">
    <li> <a (click)="do1()">do1</a> </li>
    <li> <a (click)="do2()">do2</a> </li>
    <li> <a (click)="do3()">do3</a> </li>
    ...
</ul>

I want to know when user click on ul.my_ul > li > a then I run closeOffcanvas() function.


To get what I mean: In jquery I can do this: (without any html change)

$('ul.my_ul > li > a').click(function(){
    //my close off-canvas codes
});

How can I do similar this in Angular?

I want to listen to events of a specific element list in component without change HTML.

note: please don't solve the example problem. it isn't my problem. it is only an example to explain my mean.

Upvotes: 1

Views: 3602

Answers (4)

Saeed sdns
Saeed sdns

Reputation: 980

Well, I summarize the final and best answer:

First, we access the elements with ElementRef, like this:

const elements = this.elementRef.nativeElement.querySelectorAll("ul.class_name li > a");

then we listen to element's events with Renderer2, like this:

elements.forEach( element => {
    this.renderer.listen(element, "click", event => {
        //do something...
    });
});

Don't forget to import these:

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

and:

constructor( private elementRef:ElementRef, private renderer:Renderer2 ){ }

Upvotes: 4

Stratubas
Stratubas

Reputation: 3067

It seems you can use ElementRef to select elements in the current component, like this:

@Component({
  selector: "my-app",
  template: `
    <ul>
      <li><a (click)="do1()">do1</a></li>
      <li><a (click)="do2()">do2</a></li>
    </ul>
  `
})
export class AppComponent implements OnInit, OnDestroy {

  constructor(private elementRef: ElementRef) {}

  ngOnInit() {
    const elements = this.elementRef.nativeElement.querySelectorAll("ul > li > a");
    elements.forEach(element => {
      element.addEventListener("click", this.doCommon);
    });
  }

  ngOnDestroy() {
    const elements = this.elementRef.nativeElement.querySelectorAll("ul > li > a");
    elements.forEach(element => {
      element.removeEventListener("click", this.doCommon);
    });
  }

  do1() { console.log("do1"); }
  do2() { console.log("do2"); }
  doCommon() { console.log("common"); }

}

If you don't want to limit your query to the contents of the current component, you can replace this.elementRef.nativeElement with document.

I haven't used it in the past, but it works fine in my StackBlitz project.

Maybe this whole ngOnDestroy() to prevent memory leaks isn't necessary in modern browsers, but I still feel a little safer by doing it :)

Upvotes: 2

d-h-e
d-h-e

Reputation: 2558

You can using HostListener, Renderer2 or you can give the ul-element the click event.

Here is an example for the last solution:

https://stackblitz.com/edit/angular-gfnohf?file=src%2Fapp%2Fapp.component.ts


With the event, you can see, which element was clicked

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

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

  do1() {
    console.log('do1');
  }
  do2() {
    console.log('do2');
  }
  do3() {
    console.log('do3');
  }  

  closeOffcanvas(event) {
    if(event.target.matches('a'))
    {
      console.log('user clicked on: ', event.target, 'with name: ', event.target.innerHTML);
    }    
  }
}
<ul class="my_ul" (click)="closeOffcanvas($event)">
    <li> <a (click)="do1()">do1</a> </li>
    <li> <a (click)="do2()">do2</a> </li>
    <li> <a (click)="do3()">do3</a> </li>
</ul>

update example without changing html

It is better not binding an event for all list items. The performance is much greater if you only bind a wrapper-element and check if the event.target is the right children-element

https://stackblitz.com/edit/angular-oqjstq?file=src%2Fapp%2Fapp.component.ts


Upvotes: 1

monogate
monogate

Reputation: 1439

Simply get the parent reference, treat the parent as a collection, iterate over it and assign event listeners. (In Angular syntax you can use HostListener).

Example: https://stackblitz.com/edit/angular-6fsjhu?file=src%2Fapp%2Fapp.component.html

Upvotes: 0

Related Questions