I am trying to write a Angular2 attribute directive to modify the behaviour of certain elements. More specifically I want to apply an attribute to certain elements that have click handlers and prevent the bound function to be executed under certain conditions.
So now I have an element e.g.:
<button (click)="onClick(param1, param2)"></button>
onClick is a function declared on the component that hosts the button element doing some work.
What I would like to do is write something like:
<button (click)="onClick(param1, param2)" online-only></button>
and have a directive like:
@Directive({
selector: '[online-only]',
})
export class OnlineOnlyDirective {
@HostListener('click', ['$event'])
onClick(e) {
if(someCondition){
e.preventDefault();
e.stopPropagation();
}
}
}
But click handler is executed first, thus not giving my directive the opportunity to stop its execution.
A second approach I thought about was replacing (click) with my own handler e.g.( [onlineClick]="onClick" ) and execute the passed function when the directive thinks fit, but this way I cannot pass params to onClick function and is a bit weirder to look at.
Do you have any thoughts on doing something like that?
So far this is not possible in a way you wanted to do that (just using (click) binding). This is because all events registered via Angular -> by (click) binding, @HostListner, are proxied via single listener. That's why calling stopPropagation or more correctly in this case stopImmediatePropagation doesn't work, as you don't have separate event listeners any more. Please reference this issue for more details: https://github.com/angular/angular/issues/9587.
I don't know of a way to force Angular to execute a certain event handler first. A workaround might be to use a custom event like:
<button (myClick)="onClick(param1, param2)" online-only></button>
@Directive({
selector: '[myClick]',
})
export class OnlineOnlyDirective {
@Output() myClick: EventEmitter = new EventEmitter();
@HostListener('click', ['$event'])
onClick(e) {
if(someCondition){
e.preventDefault();
e.stopPropagation();
} else {
this.myClick.next(e);
}
}
}
I recently went through something similar that I wanted to do and the suggested answer did not work maybe because I had the click handler on a custom component. This is what I did.
<button (myClick)="onClick(param1, param2)" online-only></button>
Or
<my-cmp (myClick)="onClick(param1, param2)" online-only></my-cmp>
The directive can look like so:
@Directive({
selector: '[online-only]',
})
export class OnlineOnlyDirective implements OnInit {
constructor(private el: ElementRef) { }
ngOnInit() {
if (someCondition) {
this.el.nativeElement.removeAllListeners('click');
}
}
}
This will remove the click event listener on the component or element. This won't be reactive, so if someCondition
changes there has to be a way to put the click listener back. I did not need this use case though.
This worked for me in development but did not work for me when the code got minified in production environment.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With