2

I was having kittens about the number of global Angular change detections every time an event goes off, and I found Angular 2 runOutsideAngular still change the UI and I adopted the 'OutSideEventHandlerDirective' strategy.

However, now I have a file with 6 directives in it, each for a different event type.

What I'd like to do is pass the event type into the directive; i.e. set @Input() event: string = 'my event type' inside the directive from the template html.

Is this even possible?

So, thankyou for the answers so far. Added as an edit to have nicer presentation than a comment:

Things to learn:

1/ @input () xyz: string = 'hello' - you can set this input in the template using (outSideEventHandler)="onEv($event)" [xyz]="goodbye"

2/ @input ('abc') xyz: string = 'hello' - 'renames' the thing you use to refer to the input, i.e. now you use [abc]="goodbye"

Addition to the question:

3/ I have use of two or more of these 'outside' directives on the same element. if you have (outSideEventHandler)="onEvent1($event)" [event]="mousedown" (outSideEventHandler)="onEvent2($event)" [event]="mouseup" - then you get two copies of the directive, but both use event=mousedown.

I can create two (or more) copies of the directive (see code), and then have separate unique named inputs for each. But is this the only way:?

  (outSideEventHandler1)="onTestDirective($event)" 
  [outSideEvent1]="'mousedown'" 
  (outSideEventHandler2)="onTestDirective($event)" 
  [outSideEvent2]="'mouseup'" 
Simon H
  • 373
  • 4
  • 7

2 Answers2

3
Directive({
    selector: '[testDirective]'
})
export class TestDirective implements OnInit {

    @Input() testDirective: string;

    @Input() otherInput: string = 'Saving'; // if you need more than one input
}

template

<button [testDirective]="<event-name>" // this will add the directive and also set the input
        [otherInput]="<other-input>"> // other input
</button>
Sachin S
  • 62
  • 3
2

You can add additional parameters by using aliases with the @Input decorator.

Here is a sample how you could extend the directive from the linked post with another parameter.

import { NgZone, ElementRef, EventEmitter, Input, Output, Directive } from '@angular/core'

@Directive({
  selector: '[outSideEventHandler]'
})
export class OutSideEventHandlerDirective {
  private handler: Function;

  @Input() event: string = 'click'; // pass desired event
  @Output('outSideEventHandler') emitter = new EventEmitter();
  @Input('outSideEventType') type = 'default type';

  constructor(private ngZone: NgZone, private elRef: ElementRef) {}

  ngOnInit() {
    console.log(this.type);
    this.ngZone.runOutsideAngular(() => {
      this.handler = $event => this.emitter.emit($event);
      this.elRef.nativeElement.addEventListener(this.event, this.handler);
    });
  }

  ngOnDestory() {
    this.elRef.nativeElement.removeEventListener(this.event, this.handler);
  }
}

You can then use it in this way:

<button (outSideEventHandler)="onTestDirective()" [outSideEventType]="'My type'">Test Directive</button>

Here is also a link to a StackBlitz sample that implements it. It outputs the type into the console log so you can se it is applied.

Here is also a solution for multiple event handler registration to answer also your addition. To do this I think the best solution is to have just the default input parameter and send an array of event name and handler pairs to the directive. You can for example create an object EventHandler:

export class EventHandler {
  public event: string;
  public handler: EventListener;
}

Then change the directive to accept just one default input that is an array of EventHandler objects.

import { NgZone, ElementRef, EventEmitter, Input, Output, Directive } from '@angular/core';
import { EventHandler } from './event-handler';

@Directive({
  selector: '[outSideEventHandlers]'
})
export class OutSideEventHandlerDirective {
  @Input() public outSideEventHandlers: EventHandler[];


  constructor(private ngZone: NgZone, private elRef: ElementRef) {}

  ngOnInit() {
    this.ngZone.runOutsideAngular(() => {
      for (let eventHandler of this.outSideEventHandlers) {
        console.log(eventHandler.event);
        this.elRef.nativeElement.addEventListener(eventHandler.event, eventHandler.handler);
      }

    });
  }

  ngOnDestory() {
    for (let eventHandler of this.outSideEventHandlers) {
        this.elRef.nativeElement.removeEventListener(eventHandler.event, eventHandler.handler);
      }
  }
}

Then you use it in the template in this way:

<button [outSideEventHandlers]="[{ event: 'click', handler: onClick }, { event: 'mousedown', handler: onMouseDown }, { event: 'mouseup', handler: onMouseUp}]">Test Directive</button>

This way you can add as many handlers as you want on the same element without any additional properties on the directive. Here is a link to a StackBlitz sample implementing this approach.

Aleš Doganoc
  • 11,568
  • 24
  • 40
  • thankyou for a complete answer with example; I've asked a dev to take a detailed look and see if this suits our scenario; will pass back and let you all know :) – Simon H Dec 21 '18 at 20:53
  • ok, played with the code, see modification to the question; almost there :) – Simon H Dec 22 '18 at 09:19
  • I have expanded the answer with a different solution to address you addition to the question. – Aleš Doganoc Dec 22 '18 at 22:50
  • innovative approach. This works very well for me..... Notes: 1/ you can only have once instance of a directive on an element, so the passing of an array into the directive is a good solution. 2/ strangely when using the Angular EventEmitter to send the event, you can log the event and see all internals, but with this solution we are seeing the raw browser event, for which console.log does not list the content of the event; however logging ev.altKey works and proves they are there - so we actually probably have a performance boost over the Angular version. Thanks again AlesD :) – Simon H Dec 23 '18 at 09:04
  • My final version: [link](https://stackblitz.com/edit/directive-parameters-plmogi) - just added ability to use object or array of objects. – Simon H Dec 23 '18 at 09:21