Angular — An extremely simple and easy implementation of drag and drop list reordering without library
--
Drag and drop list reordering is a very common feature of webpages with lists of data, such as Trello, Jira, etc. It is also a frequently asked question during frontend developer interviews. It was once one of the most difficult features to implement due to huge amount of DOM manipulations and overlap calculations. Yet, with the help of the HTML drag and drop API and modern frontend frameworks, this feature now only needs tens of lines to implement.
Here I am going to go through an example on implementing the drag and drop reordering feature in an exiting page with a list of data.
Implementation
Say we have a list of data in our page:
@Component({
...
template: `
<div class="card" *ngFor="let row of data">
{{ row }}
</div>
`,
styles: [`
.card {
border: 1px solid black;
padding: 4px;
margin: 8px 0;
}
`]
})
export class ListComponent {
data: string[] = [...];
}
The first step is to connect the drag and drop API with the component.
@Component({
...
template: `
<div class="drag-wrapper"
*ngFor="let row of data; let index = index"
[draggable]="true" (dragstart)="onDragStart(index)"
(dragenter)="onDragEnter(index)" (dragend)="onDragEnd()">
<div class="card">{{ row }}</div>
</div>
`,
styles: [`
.drag-wrapper {
padding: 4px 0;
}
.card {
border: 1px solid black;
padding: 4px;
}
`]
})
export class ListComponent {
data: string[] = [...]; ...
}
First, we enable the drag and drop API on the given items via binding true
to [draggable]
. For the three events, dragstart
and dragend
are pretty self-descriptive which fire when the drag starts and ends, while dragenter
refers to the moment when the bound items collide with the item that is currently being dragged. Besides, the reason to wrap the items with wrappers is to get rid of the margin, which make the detection of collision unsmooth.