3

I have a directive that listens to some events using the host property of the @directive decorator. This works great, but I don't need all events at all times.

For example, I'm only interested in (document: mouseup) at certain times (namely after a mousedown on my element).

What is the angular way to register and unregister to events dynamically?

Stefan
  • 4,187
  • 1
  • 32
  • 38

2 Answers2

3

If you register imperatively you can unregister the same way. AFAIK for declaratively added listeners there is no way to unregister.

import {DOM} from 'angular2/platform/common_dom.dart';

DOM
    .getGlobalEventTarget('window')
    .addEventListener('message', function, false);
    //.remove...

See also https://github.com/angular/angular/issues/6904

You could also just use

document.addEventListener
        // remove ...

but direct DOM access is discouraged. But in the comments of the linked issue they seem to see the first approach as direct DOM access as well.

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • Fair enough. How would I do that? Can I just use addEventListener()? I recall some $apply magic requirements in angularjs. Also, is there a $destroy equivalent? I guess I'll be needing that too. – Stefan Feb 10 '16 at 11:54
2

Not sure if you've seen the 'draggable' example that is available as part of some angular class. There is an adapted version of it in this answer. While it doesn't exactly do what you ask - ie register and de-register mousemove depending on whether we are currently dragging or not - it is one way of doing a draggable directive in angular 2.

My own slightly more general purpose version goes like this:

import {Directive, EventEmitter, HostListener, Output} from 'angular2/core';
import {Observable} from 'rxjs/Observable';

@Directive({
  selector: '[draggable]'
})
export class DraggableDirective {

  @Output() mousedrag: Observable<{x: number, y: number}>;
  @Output() dragend = new EventEmitter<void>();
  mousedown = new EventEmitter<MouseEvent>();
  mousemove = new EventEmitter<MouseEvent>();
  dragActive = false;

  @HostListener('document:mouseup', ['$event'])
  onMouseup(event) {
    if(this.dragActive) {
      this.dragend.emit(null);
      this.dragActive = false;
    }
  }

  @HostListener('mousedown', ['$event'])
  onMousedown(event: MouseEvent) {
    this.mousedown.emit(event);
  }

  @HostListener('document:mousemove', ['$event'])
  onMousemove(event: MouseEvent) {
    if(this.dragActive) {
      this.mousemove.emit(event);
      return false;
    }
  }

  constructor() {
    this.mousedrag = this.mousedown.map((event) => {
      this.dragActive = true;
      return { x: event.clientX, y: event.clientY };
    }).flatMap(mouseDownPos => this.mousemove.map(pos => {
      return { x: pos.clientX - mouseDownPos.x, y: pos.clientY - mouseDownPos.y };
    }).takeUntil(this.dragend));
  }
}

Take that with a pinch of salt as I am currently chasing a memory leak which seems to be related to this directive. I will update if I find an issue.

Community
  • 1
  • 1
LOAS
  • 7,161
  • 2
  • 28
  • 25