Search code examples
angularinternet-explorerdrag-and-dropdraggableangular2-directives

Angular 2 directive: Unexpected call to method or property access


I am following this example to implement drag and drop functionality on an Angular 2 cli project. All works fine on Chrome. On IE 11 though I get exceptions when I start dragging an element.

enter image description here

Any suggestions on how to solve this?


Solution

  • This seems to be a known bug of Internet Explorer caused by calling dataTransfer.setData with anything but 'Text'. Unfortunately this example was doing exactly that and failed in IE. I have implemented a different approach - using an Angular service to track the current zone. Here is the updated Plunkr.

    import { Injectable } from '@angular/core';
    
    @Injectable()
    export class DragService {
      private zone: string;
    
      startDrag(zone: string) {
        this.zone = zone;
      }
    
      accepts(zone: string): boolean {
        return zone == this.zone;
      }
    }
    

    Draggable

    @Directive({
      selector: '[myDraggable]'
    })
    export class DraggableDirective {
      constructor(private dragService: DragService) {
    
      }
    
      @HostBinding('draggable')
      get draggable() {
        return true;
      }
    
      @Input()
      set myDraggable(options: DraggableOptions) {
        if (options) {
          this.options = options;
        }
      }
    
      private options: DraggableOptions = {};
    
      @HostListener('dragstart', ['$event'])
      onDragStart(event) {
        const { zone = 'zone', data = {} } = this.options;
    
        this.dragService.startDrag(zone);
    
        event.dataTransfer.setData('Text', JSON.stringify(data));
      }
    }
    

    Drop Target

    @Directive({
      selector: '[myDropTarget]'
    })
    export class DropTargetDirective {
      constructor(private dragService: DragService) {
    
      }
    
      @Input()
      set myDropTarget(options: DropTargetOptions) {
        if (options) {
          this.options = options;
        }
      }
    
      @Output('myDrop') drop = new EventEmitter();
    
      private options: DropTargetOptions = {};
    
      @HostListener('dragenter', ['$event'])
      @HostListener('dragover', ['$event'])
      onDragOver(event) {
        const { zone = 'zone' } = this.options;
    
        if (this.dragService.accepts(zone)) {
           event.preventDefault();
        }
      }
    
      @HostListener('drop', ['$event'])
      onDrop(event) {
        const data =  JSON.parse(event.dataTransfer.getData('Text'));
    
        this.drop.next(data);
      }
    }