Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeScript | JavaScript | Angular 2: Dynamically Set @HostListener Argument

Dynamically Setting the @HostListener's Arguments

I have a directive which needs to listen for any event provided declaratively by the engineer. Here's an example:

import { Directive, Input, ElementRef, HostListener, OnInit } from '@angular/core';
//--
import { Sandbox } from '../../../sandbox';

@Directive({ selector: '[addItem]' })
class AddNewItemDirective implements OnInit {
    @Input('addItem') data;
    @Input() on: string = 'click';

    constructor(private $: Sandbox, private element: ElementRef) { }
    ngOnInit() { console.log('@INIT', this); }

    @HostListener('click', ['$event']) handleEvent(e) {
        console.log('add-item', e);
    }
}

export { AddNewItemDirective };

Here's its usage:

<button class="btn btn-primary" [addItem]="{ name: 'Jeffrey' }" on="focus">Add New Item</button>

This works fine. However, my intuition told me I should be able to dynamically set the HostListener's arguments at render time based upon an input parameter:

@Directive({ selector: '[addItem]' })
class AddNewItemDirective implements OnInit {
    @Input('addItem') data;
    @Input() on: string = 'click';

    constructor(private $: Sandbox, private element: ElementRef) { }
    ngOnInit() { console.log('@INIT', this); }

    @HostListener(this.on, ['$event']) handleEvent(e) {
        console.log('add-item', e);
    }
}

Of course, this.on would not be overwritten with 'focus' until the time ngOnInit is invoked. To my surprise, this.on throws an error because undefined has no property 'on'. So when my directive class is instantiated, for whatever reason, this === undefined.

I found one similar question here, though, its looking to dynamically modify HostListener at runtime while I just need it modified at compile/render/instantiation time.

Can someone please shed light on how I can accomplish this?

Thx

like image 731
Cody Avatar asked Dec 11 '22 12:12

Cody


1 Answers

HostListener is not dynamic, it can not be changed at runtime. You should use Renderer class, which provides listen method:

@Input()
public on:string;
private dispose:Function;

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

ngOnInit(){
    this.dispose = this.renderer.listen(this.elementRef.nativeElement, this.on, e => console.log(e));
}

ngOnDestroy(){
     this.dispose();
}
like image 94
kemsky Avatar answered Dec 13 '22 22:12

kemsky