<!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];
}
}
}
}
}