Reputation: 980
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.
closeOffcanvas()
into the do1()
or do2()
or ...directive
.(click)="do1(); closeOffcanvas()"
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?
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
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
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
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
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