
Is it possible in a web application to display data in tabular form and allowing the user to move columns with the drag and drop? In this article, I will tell you about my experience in an Angular project.

Let’s start with basics using the HTML tag <table>:
<table class="table" >
<thead class="thead-dark" >
<tr >
<th *ngFor="let column of columns" >
{{column}}
</th >
</tr >
</thead >
<tbody >
<tr *ngFor="let row of rows" >
<td *ngFor="let column of columns" >
{{row[column]}}
</td >
</tr >
</tbody >
</table >
columns is a strings array used to set columns’ headers, whereas rows is an object array.
Data are present, but it still lacks the possibility to move columns.
Dragula
There’s a library that allows us to add this functionality with only a few code’s changes: its name is ng2-dragula and it is the official wrapper of the open-source library dragula.
Its page on github is:
https://github.com/valor-software/ng2-dragula
First of all, we have to add it with the command:
npm install ng2-dragula
And then add some configuration lines to make it usable.
- add (window as any).global = window; to the polyfills.ts, file which allows Angular to maintain the compatibility with different browsers;
- Add DragulaModule.forRoot() in the imports of app.module.ts;
- add in your .css file (i.e.:style css) the following style’s regulations:
/* in-flight clone */
.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);
pointer-events: none;
}
/* high-performance display:none; helper */
.gu-hide {
left: -9999px !important;
}
/* added to mirrorContainer (default = body) while dragging */
.gu-unselectable {
-webkit-user-select: none !important;
-moz-user-select: none !important;
-ms-user-select: none !important;
user-select: none !important;
}
/* added to the source element while its mirror is dragged */
.gu-transit {
opacity: 0.2;
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=20)";
filter: alpha(opacity=20);
}
The library offers the dragula directive that, when applied to a container, permits to enable the drag and drop of all its sons. It is possible to pass a string to this directive, that defines the name of the group to which they belong. In this way, it is possible to assign the same group to different containers, enabling the drag and drop of an element among containers belonging to the same group.
It is possible to save the changes made with the drag and drop using the two-way binding with [(dragulaModel)]=”…” or sign up for DragulaService dropModel. I choose to use the first method since I don’t need to do more operations on data.
<table class="table" >
<thead class="thead-dark" >
<tr dragula="table_columns" [(dragulaModel)]="columns" >
<th *ngFor="let column of columns" >
{{column}}
</th >
</tr >
</thead >
<tbody >
<tr *ngFor="let row of rows" >
<td *ngFor="let column of columns" >
{{row[column]}}
</td >
</tr >
</tbody >
</table >
Now it’s possible to drag a column and put it before or after another one.
If we have a table with a number of columns that requires the use of a scrollbar on the page, the functioning of the library is not the expected one. For example, if we want to move a column from the top positions to one of the lasts, we need to make a partial drag and drop

Dom-autoscroller
It is possible, in that case, to use the help of an open-source library, that will take care to automatically scroll the table, depending on the mouse position relative to the scrollable element.
You can find the public repository here: https://github.com/hollowdoor/dom_autoscroller
Now we can install the library in our project:
npm install dom-autoscroller
It is necessary to add following command lines, to enable one or more elements to the scroll.
import * as autoScroll from 'dom-autoscroller';
autoScroll([elem], {
margin: 30,
maxSpeed: 10,
scrollWhenOutside: true,
autoScroll() {
return this.down;
}
});
where elem is the element on which we want to apply the automatic scroll.
We can now add the following code lines in the ngAfterViewInit of our component:
ngAfterViewInit(): void {
const elem = Array.from(this.elemRef.nativeElement
.querySelectorAll('.table-container'));
autoScroll(elem, {
margin: 30,
maxSpeed: 10,
scrollWhenOutside: true,
autoScroll() {
return this.down;
}
});
}
But first we should add among the builder parameters
private elemRef: ElementRef
that allows us to obtain a reference to our scrollable element.
Now the functioning is right, but we can intensify the reusability of our code creating a directive:
import { Directive, OnInit, ElementRef } from '@angular/core';
import * as autoScroll from 'dom-autoscroller';
@Directive({ selector: '[appAutoScroll]' })
export class AutoScrollDirective implements OnInit {
constructor(private elemRef: ElementRef) { }
ngOnInit(): void {
autoScroll([this.elemRef.nativeElement], {
margin: 30,
maxSpeed: 10,
scrollWhenOutside: true,
autoScroll() {
return this.down;
}
});
}
}
And then apply it to the table container:
<div class="text-center mt-5">
<h1>
Welcome to {{ title }}!
</h1>
</div>
<div class="p-5 m-5">
<div class="table-responsive table-container" appAutoScroll>
<table class="table">
<thead class="thead-dark">
<tr dragula="table_columns" [(dragulaModel)]="columns">
<th *ngFor="let column of columns">
{{column}}
</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let row of rows">
<td *ngFor="let column of columns">
{{row[column]}}
</td>
</tr>
</tbody>
</table>
</div>
</div>
The whole code can be moved in a component, that can be used in any part of the project: we will pass to it the data to be displaied as input.
The code of the demo is available on github at following address:
See you next!