Event OnDrop for PrimeNG Table
Tags: angular, tutorial, programming, primeng
Published: 2023-02-06 18:28:42

## Introduction

Hei there!
I was working in a transport and logistic management system, in particular on a form to manage the delivery: which truck has to do delivery, which driver, when, which deliveries, etc.
I used for it a PrimeNG table with the reorder property to assign deliveries to the trip but there are a few conditions that the PrimeNg table does not handle: some deliveries cannot be reordered.
The order is important in a delivery trip and I cannot put a delivery not yet delivered before a delivered one.
So I had to control the onDrop event of the table and can set up conditions, reject the reorder or accept it. The default PrimeNG table does not have this possibility and after a little research on the internet the only thing useful that I found was a comment in a PrimeNG forum: "play with the source code". So I play with that and I found a way!

## PrimeNG Table

```html
<p-table [value]="products" [columns]="cols" [reorderableColumns]="true" [tableStyle]="{'min-width': '50rem'}">
    <ng-template pTemplate="header" let-columns>
        <tr>
            <th style="width:3rem"></th>
            <th *ngFor="let col of columns" pReorderableColumn>
                {{col.header}}
            </th>
        </tr>
    </ng-template>
    <ng-template pTemplate="body" let-rowData let-columns="columns" let-index="rowIndex">
        <tr [pReorderableRow]="index">
            <td>
                <span class="pi pi-bars" pReorderableRowHandle></span>
            </td>
            <td *ngFor="let col of columns">
                {{rowData[col.field]}}
            </td>
        </tr>
    </ng-template>
</p-table>
```

This is a `p-table` with the reorder row drag&drop property active. 
The `[pReorderableRow]="index"` turn on the order ability of the table. 
The`<span class="pi pi-bars" pReorderableRowHandle></span>` put a handle icon to perform the drag and drop.

![drag and drop](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kqrsxl3s2pz4pbwbab7t.png)
So I started to search in PrimeNG code and I found a directive that implements this function on the table: `ReorderableRow`.

## How to use it

Ok, ok too long story, but how to use it?
It's here! It's enough to copy this directive that extends the `ReorderableRow`.

```typescript
import { ElementRef, EventEmitter, Input, NgZone, Output } from '@angular/core';
import { Directive, HostListener } from '@angular/core';
import { ReorderableRow, Table } from 'primeng/table';


@Directive({
  selector: '[pReordableRowDrop]'
})
export class ReordableRowDrop extends ReorderableRow {
  @Input() set pReordableRowDrop(value: number) {
    this.index = value;
  }

  @Output() dropRow = new EventEmitter<any>();
  override index = 0;
  tmsEventDrop: any;
  dtDraggedRowIndex?: number;
  dtDroppedRowIndex?: number;

  constructor(public override dt: Table, public override el: ElementRef, public override zone: NgZone) {
    super(dt, el, zone);
  }

  override onDrop(event: any): void {

    if (this.isEnabled() && this.dt.rowDragging) {
      this.dtDraggedRowIndex = this.dt.draggedRowIndex;
      this.dtDroppedRowIndex = this.dt.droppedRowIndex;
      const dropIndex = this.dt.draggedRowIndex > this.dt.droppedRowIndex ? this.dt.droppedRowIndex : this.dt.droppedRowIndex === 0 ? 0 : this.dt.droppedRowIndex - 1;

      this.tmsEventDrop = event;

      const eventArgs = {
        sender: this,
        draggedRowIndex: this.dt.draggedRowIndex,
        droppedRowIndex: dropIndex
      }

      this.dropRow.emit(eventArgs);


    }

    event.preventDefault();

  }

  public acceptDrop(): void {
    this.dt.draggedRowIndex = this.dtDraggedRowIndex!;
    this.dt.droppedRowIndex = this.dtDroppedRowIndex!;
    this.dt.onRowDrop(this.tmsEventDrop, this.el.nativeElement);
  }

  public rejectDrop(): void {
    //cleanup
    this.tmsEventDrop = null;
    this.dtDraggedRowIndex = undefined;
    this.dtDroppedRowIndex = undefined;
    this.dt.onRowDragLeave(this.tmsEventDrop, this.el.nativeElement);
    this.dt.onRowDragEnd(this.tmsEventDrop);
  }

}
```
This directive intercepts the OnDrop event of the table and implements an EventEmitter that raises an event at the exterior. 
`acceptDrop()` and `rejectDrop()` are methods that continue the normal flow of a reorder, the first one is to accept the reorder, and the second one is to reject because there are some mandatory operations to do.

Copy this directive into your projects and add it to the declarations in the module.

Now overwrite the `p-table`, in particulary replace `[pReorderableRow]="index"` with `[pReordableRowDrop]="index" (dropRow)="onDropRow($event)"`.
```html
 <p-table [value]="products" [columns]="cols" [reorderableColumns]="true" [tableStyle]="{'min-width': '50rem'}">
    <ng-template pTemplate="header" let-columns>
      <tr>
        <th style="width:3rem"></th>
        <th style="width:3rem">#</th>
        <th *ngFor="let col of columns" pReorderableColumn>
          {{col.header}}
        </th>
      </tr>
    </ng-template>
    <ng-template pTemplate="body" let-rowData let-columns="columns" let-index="rowIndex">
      <tr [pReordableRowDrop]="index" (dropRow)="onDropRow($event)">
        <td>
          <span class="pi pi-bars" pReorderableRowHandle></span>
        </td>
        <td>
          {{index}}
        </td>
        <td *ngFor="let col of columns">
          {{rowData[col.field]}}
        </td>
      </tr>
    </ng-template>
  </p-table>
```
and finally manage the `onDropRow($event)`:
```typescript
  onDropRow(e: any): void {
    if (this.products[e.draggedRowIndex].deliveryStatusCode! < 50 && this.products[e.droppedRowIndex].deliveryStatusCode! < 50) {
      e.sender.acceptDrop();
      this.logs.push(`SUCCESS: Reordered ${this.products[e.draggedRowIndex].name!} in position ${e.droppedRowIndex}.`);
    } else {
      e.sender.rejectDrop();
      this.logs.push(`FAILED: Impossibile move ${this.products[e.draggedRowIndex].name!} in position ${e.droppedRowIndex}. One of those items was delivered yet. :(`);
    }
  }
```
**IMPORTANT:** it is very mandatory to add an `e.sender.acceptDrop();` or `e.sender.rejectDrop();` otherwise the table have some bugs.

## Conclusion
This is a reference of a complete example project to use this directive:
{% embed https://github.com/ffex/prime-ng-table-drop-event/ %}

It's all folks! Bye!