<!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/primeng@4.0.0-rc.2/resources/primeng.min.css" />
    <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 */

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'
  },
  //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 {PrimeDragulaDirective} from './primeDragula';
import {DragulaService} from 'ng2-dragula';

import {
  DataTableModule
} from 'primeng';


@NgModule({
  imports: [ 
    BrowserModule, CommonModule, FormsModule, DataTableModule
  ],
  declarations: [ 
    AppComponent, 
  PrimeDragulaDirective
  ],
  providers: [DragulaService]
  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, AfterViewInit, ElementRef, EventEmitter, OnChanges } from '@angular/core';

export class EditableTableColumn {
  header: string;
  field: string;
  type: 'text' | 'checkbox';
  required?: boolean;
  defaultValue? = '';
}

type RowValue = any;


@Component({
  selector: 'my-app',
  templateUrl: 'src/app.component.html',
  styleUrls: ['src/app.css']
})
export class AppComponent implements OnInit {
  
  @Input() columns: Array<EditableTableColumn> = [];
  @Input() rows: RowValue[] = [];
  
  
  columns = [
    {
      header: 'Name',
      field: 'description',
      type: 'text',
      required: true,
    },
    {
      header: 'Code',
      field: 'code',
      type: 'text',
      required: true
    },
    {
      header: 'Display',
      field: 'display',
      type: 'checkbox',
      defaultValue: true
    }
  ];

  
  constructor() { }

  ngOnInit() {
    
     this.rows = [{description: 'test', code: 'WW', display: true},
      {description: 'test1', code:'WW1', display: true},
      {description: 'test2', code:'WW2', display: true},
      {description: 'test3', code:'WW3', display: true}];
  }
}
<div class="header">
  <h2>Demo NGPrime and NGDragula Angular 4</h2>
</div>

<div class="container-fluid" style="padding-top:20px;">
  <div class="row">
    <div class="col-md-12">
      <p-dataTable [value]="rows" [tableStyle]="{ 'table-layout': 'auto' }" 
       [primeDragula]="bag" [dragulaModel]="rows" 
       [dragulaOptions]="{ childContainerSelector: 'tbody', initAfterView: true }"
      #datatable>

        <p-column header="Move">
          <ng-template pTemplate="body" let-rowData="rowData">
            <div class="text-center" *ngIf="rowData !== addedRow"><i class="fa fa-bars"></i></div>
          </ng-template>
        </p-column>

        <p-column *ngFor="let col of columns" [field]="col" [header]="col.header" [style]="{ width: textColumnWidth + '%' }">
          <ng-template pTemplate="body" let-col let-rowData="rowData">
            <div *ngIf="rowData[editable]" class="editable">
              <div *ngIf="col.field.type === 'text'">
                <input pInputText class="full-width" type="text" [(ngModel)]="rowData[editData][col.field.field]" />
              </div>
              <div *ngIf="col.field.type === 'checkbox'" class="text-center">
                <input type="checkbox" [(ngModel)]="rowData[editData][col.field.field]" />
              </div>
            </div>
            <div *ngIf="!rowData[editable]">
              <div *ngIf="col.field.type === 'text'">
                {{ rowData[col.field.field] }}
              </div>
              <div *ngIf="col.field.type === 'checkbox'" class="text-center">
                <i [className]="'fa ' + (rowData[col.field.field] ? 'text-primary fa-check-square-o' : 'text-muted fa-square-o')"></i>
              </div>
            </div>
          </ng-template>
        </p-column>

        <p-column header="Edit" styleClass="edit">
          <ng-template pTemplate="body" let-col let-rowData="rowData">
            <div class="text-center">
              <span *ngIf="rowData[editable]">
          <i *ngIf="rowData !== addedRow" (click)="onRowCancel(rowData)" class="fa fa-times"></i>
          <i (click)="onRowSave(rowData)" class="fa fa-save text-primary"></i>
        </span>
              <i *ngIf="!rowData[editable]" (click)="rowData[editable] = true" class="fa fa-pencil-square-o text-primary"></i>
            </div>
          </ng-template>
        </p-column>

        <p-column header="Delete">
          <ng-template pTemplate="body" let-rowData="rowData">
            <div class="text-center"><i (click)="onRowDelete(rowData)" class="fa fa-trash-o text-danger"></i></div>
          </ng-template>
        </p-column>

      </p-dataTable>
    </div>

  </div>
</div>
import { Directive, OnChanges, AfterViewInit, OnInit, Input, ElementRef, SimpleChange } from '@angular/core';
import { DragulaService, dragula } from 'ng2-dragula';

@Directive({ selector: '[primeDragula]' })
export class PrimeDragulaDirective implements OnChanges, OnInit, AfterViewInit {
  @Input() public primeDragula: string;
  @Input() public dragulaModel: any;
  @Input() public dragulaOptions: any;
  protected container: any;
  private drake: any;
  private options: any;

  private el: ElementRef;
  private dragulaService: DragulaService;
  public constructor(el: ElementRef, dragulaService: DragulaService) {
    this.el = el;
    this.dragulaService = dragulaService;
  }
  ngOnInit(){
    this.options = Object.assign({}, this.dragulaOptions);
    this.container = this.el.nativeElement;
    
    if(!this.options.initAfterView){
      this.initialize();
    }
  }
  
  ngAfterViewInit() {
    if(this.options.initAfterView){
      this.initialize();
    }
  }
  
  //since we dont have access to the ngprime datatable body or table itself we need to bing laters in the angular event cycle
  //Once this fires we have a tbody tag to attach to and create the drag drop area from.
  //because we need to setup dragula later we needed to create our own version of the directive so we have access to the private property container.
  //If ngdragula ever changes that to protected we can just extend that directive outright and override the container.
  protected initialize(){
    
    if(this.options.childContainerSelector){
        this.container = this.el.nativeElement.querySelector(this.options.childContainerSelector);
        this.options.mirrorContainer = this.container;
      }
      
    let bag = this.dragulaService.find(this.primeDragula);
    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([this.container], this.options);
      checkModel();
      this.dragulaService.add(this.primeDragula, this.drake);
    }
  }

  public ngOnChanges(changes: { dragulaModel?: SimpleChange }): void {
    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];
        }
      }
    }
  }
}