Angular Drag and Drop - The Best Way
Angular Drag and Drop - The Best Way

In this post you will learn how to implement drag and drop inside Angular.

Finished project

Just from the start I want to say that drag n drop is not an easy feature and I highly recommend you to use a library for that. Previously I made a post about drag n drop inside React. The mail problem there was that a lot of libraries were obsolete and it was difficult to find a library which implements drag n drop effectively.

We don't have such problem inside Angular because we have an awesome Angular Material and we don't even need to look for alternatives.

Angular Material is supported by Angular itself, it is always up to date with the latest Angular and you can be sure that this library won't be obsolete.

Inside Angular Material we have 2 things. First of all we have lots of different components but also we have an Angular CDK. It means that we don't get any styling or components but just full business logic and functions that we can use for our needs. And drag and drop is one of such functions.

Which actually means that we don't need some component but just logic of dragging, dropping this is why we must install Angular CDK and not Angular Material.

npm i @angular/cdk

Our project

What we want to implement is a sortable list. So we can drag the items of the list and sort them. The only thing that I prepared for our project is a file data.ts which has a list of users that we want to sort.

export const data = [
  {
    id: 1,
    name: 'Samaria',
  },
  {
    id: 2,
    name: 'Gauthier',
  },
  {
    id: 3,
    name: 'Mellisa',
  },
  {
    id: 4,
    name: 'Arabela',
  },
  {
    id: 5,
    name: 'Devon',
  },
  {
    id: 6,
    name: 'Stacee',
  },
  {
    id: 7,
    name: 'Federica',
  },
  {
    id: 8,
    name: 'Jecho',
  },
  {
    id: 9,
    name: 'Alasteir',
  },
  {
    id: 10,
    name: 'Elston',
  },
];

We will use this data to render a list and sort it. Additionally to this data.ts I have an interface for each user.

export interface UserInterface {
  id: number;
  name: string;
}

Our first step will be to get this list of data inside our component.

import { data } from './data';

export class AppComponent {
  users: UserInterface[] = data;
}

Our next step will be to add all necessary dependencies that we need inside this component in order to implement drag n drop.

import {
  CdkDrag,
  CdkDragDrop,
  CdkDropList,
  CdkDropListGroup,
} from '@angular/cdk/drag-drop';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
  standalone: true,
  imports: [
    RouterOutlet,
    RouterLink,
    CommonModule,
    CdkDropListGroup,
    CdkDropList,
    CdkDrag,
  ],
})
export class AppComponent {
  ....
}

Here we added CdkDrag, CdkDropList, CdkDropListGroup and CdkDragDrop. These are all directives that we need in our component.

Writing markup

Our next step is to write some markup.

<div cdkDropListGroup class="users-container">
  <div
    cdkDropList
    [cdkDropListData]="users"
    class="users-list"
    (cdkDropListDropped)="drop($event)"
  >
    <div *ngFor="let user of users" cdkDrag class="user">{{ user.name }}</div>
  </div>
</div>

Here we created a cdkDropListGroup which is our main container. Inside we created a cdkDropList where we provided our users as an array in cdkDropListData. Also we have a callback cdkDropListDropped which happens after we do drag and drop. Inside this list container we are rendering just a normal list but on each item we attach cdkDrag directive.

We also wrote several classes and we need to write some styling for our elements.

.users-container {
  width: 400px;
  max-width: 100%;
  margin: 0 25px 25px 0;
  display: inline-block;
  vertical-align: top;
}

.users-list {
  border: solid 1px #ccc;
  min-height: 60px;
  background: white;
  border-radius: 4px;
  overflow: hidden;
  display: block;
}

.user {
  padding: 20px 10px;
  border-bottom: solid 1px #ccc;
  color: rgba(0, 0, 0, 0.87);
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  box-sizing: border-box;
  cursor: move;
  background: white;
  font-size: 14px;
}

.user:last-child {
  border: none;
}

.cdk-drag-preview {
  box-sizing: border-box;
  border-radius: 4px;
  box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2),
    0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12);
}

.cdk-drag-placeholder {
  opacity: 0;
}

.cdk-drag-animating {
  transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
}

.users-list.cdk-drop-list-dragging .user:not(.cdk-drag-placeholder) {
  transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
}

Here we styled not only our elements but several classes which are added by CDK itself. All these styles will make our drag understandable and clear for user because of placeholders, animations and shadows.

Adding drop functions

Previously we provided drop function inside our markup but we didn't create this function yet.

export class AppComponent {
  users: UserInterface[] = data;

  drop(event: CdkDragDrop<UserInterface[]>): void {
    moveItemInArray(
      event.container.data,
      event.previousIndex,
      event.currentIndex
    );
  }
}

Inside drop we get an event with all needed information. Like what element we are dragging and with what index we need to swap it. Luckily for us we don't need to move elements in the array ourselves. With moveItemInArray function which CDK provides we can define where we need to move element. It does all heavy lifting for us and will update our list of users.

Finished project

As you can see in browser our drag and drop is fully implemented with all necessary styling.

And actually if you want to learn Angular with NgRx from empty folder to a fully functional production application make sure to check my Angular and NgRx - Building Real Project From Scratch course.

📚 Source code of what we've done