Cody
Cody

Reputation: 10015

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

Upvotes: 5

Views: 4018

Answers (1)

kemsky
kemsky

Reputation: 15270

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();
}

Upvotes: 16

Related Questions