<!DOCTYPE html>
<html>

  <head>
    <base href="." />
    <title>angular2 playground</title>
    <link rel="stylesheet" href="dragula.css" />
    <link rel="stylesheet" href="style.css" />
    <script src="https://unpkg.com/zone.js/dist/zone.js"></script>
    <script src="https://unpkg.com/zone.js/dist/long-stack-trace-zone.js"></script>
    <script src="https://unpkg.com/reflect-metadata@0.1.3/Reflect.js"></script>
    <script src="https://unpkg.com/systemjs@0.19.31/dist/system.js"></script>
    <script src="config.js"></script>
    <script>
    System.import('app')
      .catch(console.error.bind(console));
  </script>
  </head>

  <body>
    <my-app>
    loading....
  </my-app>
  </body>

</html>
/* Styles go here */

.deck {
  background: grey;
  margin: 1em;
  display: flex;
}

.card {
  background: #ffffff;
  height: 50px;
  margin: 2em;
  padding: 0.1em;
  flex: 0 0 50px;
  cursor:hand;
}
Import the dragula styles in index.html

Import the dragula script by config.js. Add to map.

Normally we'd import ng2-dragula same way but it isn't working with angular2.1.0
so download it and import as a local module. Problem is with the dependencies 
being set too low but maybe also this issue with Plunker and file extensions not
resolving?

Plunker doesn't resolve imports that omit file extensions, so update the 
ng2-dragula module and directive, change dragula.directive and dragula.provider
to dragula.directive.ts and dragula.provider.ts
System.config({
  //use typescript for compilation
  transpiler: 'typescript',
  //typescript compiler options
  typescriptOptions: {
    emitDecoratorMetadata: true
  },
  paths: {
    'npm:': 'https://unpkg.com/'
  },
  //map tells the System loader where to look for things
  map: {
    
    'app': './src',
    
    '@angular/core': 'npm:@angular/core/bundles/core.umd.js',
    '@angular/common': 'npm:@angular/common/bundles/common.umd.js',
    '@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
    '@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
    '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
    '@angular/http': 'npm:@angular/http/bundles/http.umd.js',
    '@angular/router': 'npm:@angular/router/bundles/router.umd.js',
    '@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
    
    '@angular/core/testing': 'npm:@angular/core/bundles/core-testing.umd.js',
    '@angular/common/testing': 'npm:@angular/common/bundles/common-testing.umd.js',
    '@angular/compiler/testing': 'npm:@angular/compiler/bundles/compiler-testing.umd.js',
    '@angular/platform-browser/testing': 'npm:@angular/platform-browser/bundles/platform-browser-testing.umd.js',
    '@angular/platform-browser-dynamic/testing': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic-testing.umd.js',
    '@angular/http/testing': 'npm:@angular/http/bundles/http-testing.umd.js',
    '@angular/router/testing': 'npm:@angular/router/bundles/router-testing.umd.js',
    
    'rxjs': 'npm:rxjs',
    'typescript': 'npm:typescript@2.0.2/lib/typescript.js',
    'dragula': 'https://cdnjs.cloudflare.com/ajax/libs/dragula/3.7.1/dragula.min.js'
  },
  //packages defines our app package
  packages: {
    app: {
      main: './main.ts',
      defaultExtension: 'ts'
    },
    rxjs: {
      defaultExtension: 'js'
    }
  }
});
//main entry point
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import {AppModule} from './app';

platformBrowserDynamic().bootstrapModule(AppModule)
//our root app component
import {Component, NgModule} from '@angular/core'
import {BrowserModule} from '@angular/platform-browser'

import { DragulaModule } from '../ng2-dragula/ng2-dragula.ts';
import { DragulaService } from '../ng2-dragula/dragula.provider.ts';


@Component({
  selector: 'my-app',
  templateUrl: 'src/app.html'
})
export class App {
  _el:any;
  _source:any;
  _target: any;
  _sibling: any;
  
  source: any = ['a','b','c', 'd'];

  constructor(dragulaService: DragulaService) {
    dragulaService.setOptions('my-bag', {
      isContainer: function (el) {
        return false; // only elements in drake.containers will be taken into account
      },
      moves: function (el, source, handle, sibling) {
        return true; // elements are always draggable by default
      },
      accepts: function (el, target, source, sibling) {
        
        /*
        console.log(`element: ${el.innerText.trim()}`);
        console.log(`target container: ${target.innerText.trim()}`);
        console.log(`source container: ${source.innerText.trim()}`);
        console.log(`sibling element: ${sibling.innerText.trim()}`);
        */
        //if you set this to false, item will not drop to a new position
        return true; // elements can be dropped in any of the `containers` by default
      },
      invalid: function (el, handle) {
        return false; // don't prevent any drags from initiating by default
      },
      direction: 'vertical',             // Y axis is considered when determining where an element would be dropped
      copy: false,                       // elements are moved by default, not copied
      copySortSource: false,             // elements in copy-source containers can be reordered
      revertOnSpill: true,              // spilling will put the element back where it was dragged from, if this is true
      removeOnSpill: false,              // spilling will `.remove` the element, if this is true
      mirrorContainer: document.body,    // set the element that gets mirror elements appended
      ignoreInputTextSelection: true     // allows users to select input text, see details below
    });
  
  dragulaService.drop.subscribe((value) => {
        //this.onDrop(value.slice(1));
    });
      
  dragulaService.drag.subscribe((value) => {
        //this.onDrag(value.slice(1));
    });
    
    dragulaService.over.subscribe((value) => {
        this.onOver(value.slice(1));
    });
      
  dragulaService.out.subscribe((value) => {
        this.onOut(value.slice(1));
    });
  }

  private onDrop(args) {
    let [el, target, source] = args;
    // do something else
        console.log(`ondrop element: ${el.innerText.trim()}`);
        console.log(`ondrop target container: ${target.innerText.trim()}`);
        console.log(`ondrop source container: ${source.innerText.trim()}`);
       
  }

private onOut(args) {
    let [el, target, source] = args;
    // do something else
        console.log(`ondrop element: ${el.innerText.trim()}`);
        console.log(`ondrop target container: ${target.innerText.trim()}`);
        console.log(`ondrop source container: ${source.innerText.trim()}`);
       
  }
  
  private onOver(args) {
    let [el, target, source] = args;
    // do something else
        console.log(`ondrop element: ${el.innerText.trim()}`);
        console.log(`ondrop target container: ${target.innerText.trim()}`);
        console.log(`ondrop source container: ${source.innerText.trim()}`);
       
  }
  private onDrag(args) {
    let [el, source] = args;
    // do something else
        console.log(`ondrag element: ${el.innerText.trim()}`);
        console.log(`ondrag source container: ${source.innerText.trim()}`);
  }
}

@NgModule({
  imports: [ BrowserModule, DragulaModule ],
  declarations: [ App ],
  providers: [DragulaService],
  bootstrap: [ App ]
})
export class AppModule {}


<div class="deck" [dragula]='"my-bag"' [dragulaModel]='source'>
  <div class="card" *ngFor="let item of source">
    {{item}}
  </div>
</div>


<hr> Final Output:

<ul>
  <li *ngFor="let item of source">
    {{item}}
  </li>
</ul>

<hr> Please refer Console Output to see following:

<ul>
  <li>
    Selected Element 
  </li>
  <li>
    Target Container
  </li>
  <li>
    Source Element 
  </li>
  <li>
    Sibling Element 
  </li>
</ul>
import { NgModule } from '@angular/core';
import { DragulaDirective } from './dragula.directive.ts';
import { DragulaService } from './dragula.provider.ts';

export * from './dragula.provider.ts';
export * from './dragula.directive.ts';

@NgModule({
  exports: [DragulaDirective],
  declarations: [DragulaDirective],
  providers: [DragulaService]
})
export class DragulaModule {
}
import {
  Directive,
  Input,
  ElementRef,
  OnInit,
  OnChanges,
  SimpleChange
} from '@angular/core';
import { DragulaService } from './dragula.provider.ts';
import dragula from 'dragula';

@Directive({
  selector: '[dragula]'
})
export class DragulaDirective implements OnInit, OnChanges {
  @Input('dragula') public bag:string;
  @Input() public dragulaModel:any;
  private container:any;
  private drake:any;

  public constructor(private el:ElementRef, private dragulaService:DragulaService) {
    this.container = el.nativeElement;
  }

  public ngOnInit():void {
    // console.log(this.bag);
    let bag = this.dragulaService.find(this.bag);
    let checkModel = () => {
      if (this.dragulaModel) {
        if (this.drake.models) {
          this.drake.models.push(this.dragulaModel);
        } else {
          this.drake.models = [this.dragulaModel];
        }
      }
    };
    if (bag) {
      this.drake = bag.drake;
      checkModel();
      this.drake.containers.push(this.container);
    } else {
      this.drake = dragula({
        containers: [this.container]
      });
      checkModel();
      this.dragulaService.add(this.bag, this.drake);
    }
  }

  public ngOnChanges(changes:{[propName:string]:SimpleChange}):void {
    // console.log('dragula.directive: ngOnChanges');
    // console.log(changes);
    if (changes && changes['dragulaModel']) {
      if (this.drake) {
        if (this.drake.models) {
          let modelIndex = this.drake.models.indexOf(changes['dragulaModel'].previousValue);
          this.drake.models.splice(modelIndex, 1, changes['dragulaModel'].currentValue);
        } else {
          this.drake.models = [changes['dragulaModel'].currentValue];
        }
      }
    }
  }
}
import dragula from 'dragula';
import { Injectable, EventEmitter } from '@angular/core';

@Injectable()
export class DragulaService {
  public cancel:EventEmitter<any> = new EventEmitter();
  public cloned:EventEmitter<any> = new EventEmitter();
  public drag:EventEmitter<any> = new EventEmitter();
  public dragend:EventEmitter<any> = new EventEmitter();
  public drop:EventEmitter<any> = new EventEmitter();
  public out:EventEmitter<any> = new EventEmitter();
  public over:EventEmitter<any> = new EventEmitter();
  public remove:EventEmitter<any> = new EventEmitter();
  public shadow:EventEmitter<any> = new EventEmitter();
  public dropModel:EventEmitter<any> = new EventEmitter();
  public removeModel:EventEmitter<any> = new EventEmitter();
  private events:Array<string> = [
    'cancel',
    'cloned',
    'drag',
    'dragend',
    'drop',
    'out',
    'over',
    'remove',
    'shadow',
    'dropModel',
    'removeModel'
  ];
  private bags:Array<any> = [];

  public add(name:string, drake:any):any {
    let bag = this.find(name);
    if (bag) {
      throw new Error('Bag named: "' + name + '" already exists.');
    }
    bag = {
      name: name,
      drake: drake
    };
    this.bags.push(bag);
    if (drake.models) { // models to sync with (must have same structure as containers)
      this.handleModels(name, drake);
    }
    if (!bag.initEvents) {
      this.setupEvents(bag);
    }
    return bag;
  }

  public find(name:string):any {
    for (let i = 0; i < this.bags.length; i++) {
      if (this.bags[i].name === name) {
        return this.bags[i];
      }
    }
  }

  public destroy(name:string):void {
    let bag = this.find(name);
    let i = this.bags.indexOf(bag);
    this.bags.splice(i, 1);
    bag.drake.destroy();
  }

  public setOptions(name:string, options:any):void {
    let bag = this.add(name, dragula(options));
    this.handleModels(name, bag.drake);
  }

  private handleModels(name:string, drake:any):void {
    let dragElm:any;
    let dragIndex:number;
    let dropIndex:number;
    let sourceModel:any;
    drake.on('remove', (el:any, source:any) => {
      if (!drake.models) {
        return;
      }
      sourceModel = drake.models[drake.containers.indexOf(source)];
      sourceModel.splice(dragIndex, 1);
      // console.log('REMOVE');
      // console.log(sourceModel);
      this.removeModel.emit([name, el, source]);
    });
    drake.on('drag', (el:any, source:any) => {
      dragElm = el;
      dragIndex = this.domIndexOf(el, source);
    });
    drake.on('drop', (dropElm:any, target:any, source:any) => {
      if (!drake.models || !target) {
        return;
      }
      dropIndex = this.domIndexOf(dropElm, target);
      sourceModel = drake.models[drake.containers.indexOf(source)];
      // console.log('DROP');
      // console.log(sourceModel);
      if (target === source) {
        sourceModel.splice(dropIndex, 0, sourceModel.splice(dragIndex, 1)[0]);
      } else {
        let notCopy = dragElm === dropElm;
        let targetModel = drake.models[drake.containers.indexOf(target)];
        let dropElmModel = notCopy ? sourceModel[dragIndex] : JSON.parse(JSON.stringify(sourceModel[dragIndex]));

        if (notCopy) {
          sourceModel.splice(dragIndex, 1);
        }
        targetModel.splice(dropIndex, 0, dropElmModel);
        target.removeChild(dropElm); // element must be removed for ngFor to apply correctly
      }
      this.dropModel.emit([name, dropElm, target, source]);
    });
  }

  private setupEvents(bag:any):void {
    bag.initEvents = true;
    let that:any = this;
    let emitter = (type:any) => {
      function replicate():void {
        let args = Array.prototype.slice.call(arguments);
        that[type].emit([bag.name].concat(args));
      }

      bag.drake.on(type, replicate);
    };
    this.events.forEach(emitter);
  }

  private domIndexOf(child:any, parent:any):any {
    return Array.prototype.indexOf.call(parent.children, child);
  }
}
/* default dragula styles */
.gu-mirror {
  position: fixed !important;
  margin: 0 !important;
  z-index: 9999 !important;
  opacity: 0.8;
  -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=80)";
  filter: alpha(opacity=80);
}
.gu-hide {
  display: none !important;
}
.gu-unselectable {
  -webkit-user-select: none !important;
  -moz-user-select: none !important;
  -ms-user-select: none !important;
  user-select: none !important;
}
.gu-transit {
  opacity: 0.2;
  -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=20)";
  filter: alpha(opacity=20);
}