<!DOCTYPE html>
<html>

  <head>
    <title>Demo NGPrime and NGDragula Angular 4</title>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" integrity="sha384-rwoIResjU2yc3z8GV/NPeZWAv56rSmLldC3R/AZzGRnGxQQKnKkoFVhFQhNUwEyJ" crossorigin="anonymous" />
     <link rel="stylesheet" href="https://unpkg.com/primeng@4.0.0-rc.2/resources/themes/omega/theme.css" />
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" />
    
    <link rel="stylesheet" href="https://unpkg.com/@swimlane/ngx-datatable/release/index.css" crossorigin="anonymous" />
    <link rel="stylesheet" href="https://unpkg.com/@swimlane/ngx-datatable/release/themes/material.css" crossorigin="anonymous" />
    <link rel="stylesheet" href="https://unpkg.com/@swimlane/ngx-datatable/release/assets/icons.css" crossorigin="anonymous" />
    <link rel="stylesheet" href="https://unpkg.com/dragula@3.7.2/dist/dragula.css" crossorigin="anonymous" />
  </head>

  <body>
    <my-app>
      Loading...
    </my-app>
    <script src="https://unpkg.com/core-js/client/shim.min.js"></script>
    <script src="https://unpkg.com/reflect-metadata@0.1.9/Reflect.js"></script>
    <script src="https://unpkg.com/zone.js@0.8.5/dist/zone.js"></script>
    <script src="https://unpkg.com/systemjs@0.19.41/dist/system.js"></script>
    <script src="https://unpkg.com/typescript@2.2.0/lib/typescript.js"></script>
    <script src="config.js"></script>
    <script>
    System.import('app')
      .catch(console.error.bind(console));
  </script>
  </body>

</html>

/* Styles go here */

.selector{
    cursor:pointer;
}

System.config({
  transpiler: 'typescript',
  typescriptOptions: {
    emitDecoratorMetadata: true,
    experimentalDecorators: true
  },
  map: {
    app: "./src",


    '@angular/core': 'npm:@angular/core@4.0.0/bundles/core.umd.js',
    '@angular/common': 'npm:@angular/common@4.0.0/bundles/common.umd.js',
    '@angular/compiler': 'npm:@angular/compiler@4.0.0/bundles/compiler.umd.js',
    '@angular/platform-browser': 'npm:@angular/platform-browser@4.0.0/bundles/platform-browser.umd.js',
    '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic@4.0.0/bundles/platform-browser-dynamic.umd.js',
    '@angular/http': 'npm:@angular/http@4.0.0/bundles/http.umd.js',
    '@angular/router': 'npm:@angular/router@4.0.0/bundles/router.umd.js',
    '@angular/forms': 'npm:@angular/forms@4.0.0/bundles/forms.umd.js',
    '@angular/animations': 'npm:@angular/animations@4.0.0/bundles/animations.umd.js',

    '@angular/core/testing': 'npm:@angular/core@4.0.0/bundles/core-testing.umd.js',
    '@angular/common/testing': 'npm:@angular/common/bundles@4.0.0/common-testing.umd.js',
    '@angular/compiler/testing': 'npm:@angular/compiler@4.0.0/bundles/compiler-testing.umd.js',
    '@angular/platform-browser/testing': 'npm:@angular/platform-browser@4.0.0/bundles/platform-browser-testing.umd.js',
    '@angular/platform-browser-dynamic/testing': 'npm:@angular/platform-browser-dynamic@4.0.0/bundles/platform-browser-dynamic-testing.umd.js',
    '@angular/http/testing': 'npm:@angular/http@4.0.0/bundles/http-testing.umd.js',
    '@angular/router/testing': 'npm:@angular/router@4.0.0/bundles/router-testing.umd.js',

    'rxjs': 'npm:rxjs@5.1.0',
    'lodash': 'npm:lodash@4.17.4/',

    'primeng': 'npm:primeng@4.0.0-rc.2/primeng.js',
    'dragula': 'npm:dragula@3.7.2/dist/dragula.js',
    'ng2-dragula': 'npm:ng2-dragula@1.3.0/index.js',
    '@swimlane/ngx-datatable': 'npm:@swimlane/ngx-datatable/release/index.js',
  },
  //packages defines our app package
  packages: {
    app: {
      main: './main.ts',
      defaultExtension: 'ts'

    },
    rxjs: {
      defaultExtension: 'js'
    },
    lodash: {
      defaultExtension: 'js'
    },
    primeng: {
      defaultExtension: 'js'
    }
  },
  paths: {
    'npm:': 'https://unpkg.com/'
  }
});
import { NgModule } from '@angular/core';
import {CommonModule} from '@angular/common';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule }   from '@angular/forms';
import {AppComponent} from './app.component';
import {DragulaExtendedDirective} from 'dragula-extended.directive.ts';
import {DragulaModule, DragulaService} from 'ng2-dragula';
import { NgxDatatableModule, DatatableComponent } from '@swimlane/ngx-datatable';

@NgModule({
  imports: [ 
    BrowserModule, CommonModule, FormsModule, NgxDatatableModule, DragulaModule
  ],
  declarations: [ 
    AppComponent, 
    DragulaExtendedDirective
  ],
  providers: [DragulaService],
  exports: [DragulaExtendedDirective]
  bootstrap: [ AppComponent ]
})
export class AppModule {}
{
  "compilerOptions": {
    "baseUrl": "",
    "declaration": false,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "lib": ["es6", "dom"],
    "mapRoot": "./",
    "module": "es6",
    "moduleResolution": "node",
    "sourceMap": true,
    "target": "es5"
  }
}
//our root app component
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import {ROUTER_PROVIDERS, ROUTER_DIRECTIVES, RouteParams, RouteConfig, Router, LocationStrategy, HashLocationStrategy} from '@angular2/router';
import { enableProdMode } from '@angular/core';
import { AppModule }  from './app.module';

platformBrowserDynamic().bootstrapModule(AppModule);
import { Component, Input, Output, ViewEncapsulation, AfterViewInit, ElementRef, EventEmitter, OnChanges } from '@angular/core';
import * as _ from 'lodash';

@Component({
  selector: 'my-app',
  templateUrl: 'src/app.component.html',
  styleUrls: ['src/app.css'],
  encapsulation: ViewEncapsulation.None
})
export class AppComponent implements OnInit {
  
  // COMPONENT EXAMPLE

  data = [
    {id: 1, name: 'Drew' },
    {id: 2, name: 'Billy' },
    {id: 3, name: 'Rover' },
    {id: 4, name: 'Danny' },
    {id: 5, name: 'Bob' },
    {id: 6, name: 'Will' }
  ];
  
  columns = [
    {prop: 'id', name: 'ID'},
    {prop: 'name', name: 'Name'}
  ];

  constructor() { }

  ngOnInit() {
    
  }
  ngAfterViewInit(){
    
  }
  // Table 1 DRAG and DROP
  onDrop(event){
    // ngx-datatable recommends you force change detection
    let newData = event.slice();
    // OR let this.data = [...event]
    this.data = newData;
  }
  onDrag(event){
    console.log('DRAG event::', event);
  }
  
}
<div class="header">
  <h2>Dragula and Ngx-datatable</h2>
</div>

<div class="datatable-wrapper">
  
  <!-- if you want row to be draggable, just make [classSelector] empty -->
  <!-- 
  *REQUIRED attributes for 'dragula-extended.directive.ts'
  [dragulaName]  - wrap value in 'single' quotes, must be a string
  [dragulaModel] - your data
  [classSelector] - if a value, wrap it in 'single' quotes, must be a string
  (directiveDrop) - the on drop event callback
  -->
  <ngx-datatable
      class="material"
      [rows]="data"
      [columnMode]="'standard'"
      [rowHeight]="50"
      [dragulaName]="'bag'"
      [trackByProp]="id"
      [dragulaModel]="data"
      [classSelector]=""
      (directiveDrop)="onDrop($event)"
      (directiveDrag)="onDrag($event)"
      >
        <ngx-datatable-column name="Drag" [flexGrow]="0" >
            <ng-template let-column="column" ngx-datatable-header-template>
                <span >{{column.name}}</span>
            </ng-template>
            <ng-template let-row="row" ngx-datatable-cell-template>
                <span class="selector fa fa-bars"></span>
            </ng-template>
        </ngx-datatable-column>
        <ngx-datatable-column name="ID" [flexGrow]="1" >
            <ng-template let-column="column" ngx-datatable-header-template>
                <span >{{column.name}}</span>
            </ng-template>
            <ng-template let-row="row" ngx-datatable-cell-template>
                <span>{{row.id}}</span>
            </ng-template>
        </ngx-datatable-column>
        <ngx-datatable-column name="NAME" [flexGrow]="1" >
            <ng-template let-column="column" ngx-datatable-header-template>
                <span >{{column.name}}</span>
            </ng-template>
            <ng-template let-row="row" ngx-datatable-cell-template>
                <span>{{row.name}}</span>
            </ng-template>
        </ngx-datatable-column>
    </ngx-datatable>
    
</div>
import { Directive, OnChanges, AfterViewInit, EventEmitter, OnInit, Output, Input, OnDestroy, ElementRef, SimpleChange } from '@angular/core';
import { dragula, DragulaService } from 'ng2-dragula';

@Directive({ selector: 'ngx-datatable[dragulaName]' })
export class DragulaExtendedDirective implements OnChanges, OnInit, AfterViewInit, OnDestroy {
  @Input() public dragulaName: string;
  @Input() public dragulaModel: any;
  @Input() public classSelector: string = 'null';
  @Output() public directiveDrop: EventEmitter<any> = new EventEmitter<any>();
  @Output() public directiveDrag: EventEmitter<any> = new EventEmitter<any>();

  protected container: any;
  private drake: any;
  private options: any;
  private el: ElementRef;
  private dragulaService: DragulaService;
  subscriptionDrag: any = null;
  subscriptionDrop: any = null;

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

  }

  ngAfterViewInit() {
      if(this.el){
          let container = this.el;

          // Check for the row's parent node: datatable-scroller
          // This is what you want to bind Dragula to, in order to drag sort
          if(container.nativeElement.querySelector('datatable-scroller')){
              let rowParent =  container.nativeElement.querySelector('datatable-scroller');

              // Check if this Dragula already exists
              if( !this.dragulaService.find(this.dragulaName) ){

                  // Must assign the new rowParent as the container you want to pass to Dragula
                  this.container = rowParent;
                  this.initializeDragula();
              }
          }
      }
  }

  ngOnDestroy() {

      // Clear this Dragula always
      // comment out if you want to keep it
      if (this.dragulaService.find(this.dragulaName)) {
          this.dragulaService.destroy(this.dragulaName);
      }

      // Clear DRAG and DROP subscription to prevent duplicates
      if(this.subscriptionDrag){
          this.subscriptionDrag.unsubscribe();
          this.subscriptionDrag = null;
      }
      if(this.subscriptionDrop){
          this.subscriptionDrop.unsubscribe();
          this.subscriptionDrop = null;
      }
  }

  protected initializeDragula(){
      // Create new Dragula container
      let bag = this.dragulaService.find(this.dragulaName);
      if (bag) {
          this.drake = bag.drake;
          this.checkModel();
          this.drake.containers.push(this.container);
      } else {

          // Check if classSelector was specified
          // *true:
          //    - the classSelector string will be used to match the class of the element clicked
          //    - the element with the matching class name will be used to drag the row
          // *false:
          //    - no class selector will be used
          //    - the whole row will default back to being draggable
          if(this.classSelector != 'null'){
              let classSelector = this.classSelector;
              let options = {
                  moves: function (el, container, handle) {
                      return handle.className === classSelector;
                  }
              };
              this.drake = dragula([this.container], options);
          }else{
              this.drake = dragula([this.container]);
          }
          this.checkModel();
          this.dragulaService.add(this.dragulaName, this.drake);
      }

      // Set DRAG and DROP subscriptions and callbacks
      this.subscriptionDrag = this.dragulaService.drag.subscribe((value) => {
          this.drag(value.slice(1));
      });
      this.subscriptionDrop = this.dragulaService.drop.subscribe((value) => {
          const [bagName, el, target, source] = value;

          this.onDropModel(value.slice(1));
      });
  }

  private checkModel(){
      if (this.dragulaModel) {
          if (this.drake.models) {
              this.drake.models.push(this.dragulaModel);
          } else {
              this.drake.models = [this.dragulaModel];
          }
      }
  }

  private drag(args) {
      let [e, el] = args;
  }

  private onDropModel(args) {
      let [el, target, source] = args;

      // Added emitter on any DROP action
      this.directiveDrop.emit(this.dragulaModel);
  }

  public ngOnChanges(changes: { dragulaModel?: SimpleChange }): void {

      // Must update model on any changes
      // Otherwise it will fall out of sync with the 'dragulaModel'
      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];
              }
          }
      }
  }
}