Drag-and-drop with Angular Material
Published: 2022-07-17 13:02:36

by [Godwin Chinda](https://blog.openreplay.com/authors/godwin-chinda)

[Angular Material](https://material.angular.io/) is a User Interface (UI) component library that developers can use to quickly create stunning and cohesive user interfaces in their Angular projects. With Angular Material, you can find reusable and appealing UI components like Cards, Inputs, Data Tables, Datepickers, and much more. Each component is ready to be used in the default style according to the [Material Design specification](https://material.io/design). Still, you can easily customize the look of Angular Material components. The available list of [Angular Material components](https://material.angular.io/components/categories) grows with each library iteration.

Drag and drop is a platform where applications use drag-and-drop functionality on browsers. The user clicks and drags files to a droppable element (drop zone) using a mouse or touchpad, then releases the mouse button to release the files. [Angular Drag and Drop CDK](https://material.angular.io/cdk/drag-drop/overview) (Component Dev Kit) provides support for free dragging, sorting within a list, moving items between lists, animations, touch devices, custom drag handles, and more. The `@angular/cdk/drag-drop` module also allows you to create drag-and-drop interfaces quickly and declaratively.

## Getting Started

The first step is to install Angular Material UI into our Project.

```
ng add @angular/material
```

The `DragDropModule` will be imported into `NgModule`:

```javascript
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import {DragDropModule} from '@angular/cdk/drag-drop';
@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule, 
    BrowserAnimationsModule,
    DragDropModule,
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
```

Now that we've imported the module, we can create our first draggable component by using the `cdkDrag` directive.

```
<div class="box" cdkDrag>
  Drag me around
</div>
```

We can already drag and drop in our project after running the code.

![1](https://blog.openreplay.com/6e0ea486672355bcd1da892163b59d24/image01.gif)


## Creating a Drop Zone 

The next step is to build a drop zone now that we know how to drag an element. We'll utilize the new directive `cdkDropList` to do this; it will serve as a container to dump the draggable elements into. The item will return to its original location inside the drop zone if we attempt to dump it outside the drop zone.

```
<div cdkDropList>
  <div class="box" cdkDrag>
    Drag me around
  </div>
</div>
```

![2](https://blog.openreplay.com/aa680916b533197aa5203b71a423fa8d/image02.gif)

## Re-Ordering Items Inside a List

The next stage is dragging and re-ordering things inside a list after learning to make a draggable item and a drop zone. To generate the list components inside a `cdkDrop` container, we'll utilize the `*ngFor` directive.

```
<div class="box" cdkDropList>
  <div *ngFor="let item of items" cdkDrag>{{item}}</div>
</div>
```

The elements are defined as a string array in the `AppComponent`:

```
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  items = ['Football', 'Tennis', 'Basketball', 'Rugby', 'Golf']
}
```

The GIF below shows how things automatically rearrange themselves as we drag them. When we drag and drop anything, it returns to its original place.

![3](https://blog.openreplay.com/0055e08d91cd8915143885d302faf039/image03.gif)


We must implement the `cdkDropDropped` method in order to resolve this issue and save the new index when an item is dropped inside the list. When a user drops something inside the drop zone, the dropped function is always called. Its trademark is as follows:

```javascript
import {CdkDragDrop, moveItemInArray} from '@angular/cdk/drag-drop';
@Component({...})
export class AppComponent {
  title = 'dropzone';
  items = [...]
  drop(event: CdkDragDrop<string[]>) {
    moveItemInArray(this.items, event.previousIndex, event.currentIndex);
  }
}
```

The utility function `moveItemInArray` is also included in the drag and drop CDK, as shown in the code above. The array's dropped item's new index is determined using this function, rad! It's time to link the `dropped` function to the HTML's `cdkDrop` element now that we have implemented it.

```
<div class="box" cdkDropList
(cdkDropListDropped)="drop($event)">
  <div *ngFor="let item of items" cdkDrag>{{item}}</div>
</div>
```

This results in elements inside the `cdkDrop` container being draggable and reorderable. Click [here](https://beta-angular-material-io.firebaseapp.com/cdk/drag-drop/overview#reordering-lists) for a better understanding.

![4](https://blog.openreplay.com/435fe0735da2df3b851055c50f0ef117/image04.gif)



## Open Source Session Replay

_[OpenReplay](https://github.com/openreplay/openreplay) is an open-source, session replay suite that lets you see what users do on your web app, helping you troubleshoot issues faster. OpenReplay is self-hosted for full control over your data._

![replayer.png](https://static.openreplay.com/images/banner-blog.png)

_Start enjoying your debugging experience - [start using OpenReplay for free](https://github.com/openreplay/openreplay)._




## Dragging from one list to another list

Let's go one step farther and construct a straightforward task board. To do this, we will divide the `items` array into three smaller arrays: one for new items, one for active items, and one for completed items.

```javascript
incomingGoods = ['Tomatoes', 'Carrots', 'Onions', 'Pepper']

availableGoods = ['Cucumber']

soldGoods = ['Orange', 'Apple', 'Banana']
```

Three different lists must be displayed, and each list will have its own drop zone. By using the `cdkDropData` input, we can connect the arrays to a drop zone.

```
<div
  cdkDrop
  #new="cdkDrop"
  [cdkDropData]="newItems"
  [cdkDropConnectedTo]="[active]"
  (cdkDropDropped)="dropped($event)"
>
  <div *ngFor="let item of newItems" cdkDrag>{{ item }}</div>
</div>
```

A `cdkDrop` list instance can be linked to another `cdkDrop` list instance using the `[cdkDropConnectedTo]` input attribute. We won't be able to drag and drop the things to another list if we don't do this. The connections that need to be made in our task board example are as follows:

- add the `incomingGoods` to the `availableGoods` list;
- add the `availableGoods` to the `incomingGoods` and `soldGoods` list;
- add the `soldGoods` list onto the `availableGoods`;

To put it another way, you can drag a `incomingGoods` to the `availableGoods`, the `soldGoods`, or the reverse order. However, you must first move through the `availableGoods` to drag a `incomingGoods` to the `soldGoods`. Combining these results yields the following: 

```
<div cdkDropListGroup>
  <div class="container">
    <h2>Incoming Goods</h2>
    <div
    id="incoming"
      cdkDropList
      [cdkDropListData]="incomingItems"
      cdkDropListConnectedTo="available"
      class="list"
      (cdkDropListDropped)="drop($event)"
      [cdkDropListEnterPredicate]="noReturnPredicate">
      <div class="box" *ngFor="let item of incomingItems" cdkDrag>{{item}}</div>
    </div>
  </div>
  <div class="container">
    <h2>Available Goods</h2>
    <div
    id="available"
      cdkDropList
      [cdkDropListData]="availableItems"
      cdkDropListConnectedTo="sold"
      class="list"
      (cdkDropListDropped)="drop($event)"
      >
      <div class="box" *ngFor="let item of availableItems" cdkDrag>{{item}}</div>
    </div>
  </div>
  <div class="container">
    <h2>Sold Out Goods</h2>
    <div
    id="sold"
      cdkDropList
      [cdkDropListData]="soldItems"
      cdkDropListConnectedTo="available"
      class="list"
      (cdkDropListDropped)="drop($event)"
      >
      <div class="box" *ngFor="let item of soldItems" cdkDrag>{{item}}</div>
    </div>
  </div>
</div>
```

Making our `dropped` function smarter is the final phase; to meet our needs, it must transfer items from one list to another list.

```javascript
import { CdkDragDrop, moveItemInArray, transferArrayItem } from '@angular/cdk/drag-drop';

dropped(event: CdkDragDrop<string[]>) {
  if (event.previousContainer === event.container) {
    moveItemInArray(
      event.container.data,
      event.previousIndex,
      event.currentIndex
    );
  } else {
    transferArrayItem(
      event.previousContainer.data,
      event.container.data,
      event.previousIndex,
      event.currentIndex
    );
  }
}
```

It rearranges the elements in the same previous order if the container is the same. The dragged item gets transferred to the list where it is being dumped if the container differs. Once more, a useful function called `transferArrayItem` is included right out of the box. Check can the [documentation](https://beta-angular-material-io.firebaseapp.com/cdk/drag-drop/overview#transferring-items-between-lists).


![5](https://blog.openreplay.com/185a2d0f677fcfdad4271cc772d4fc38/image05.gif)

## Disable dragging

Setting the `cdkDragDisabled` input on a `cdkDrag` item will disable dragging for that particular drag item. Additionally, you can disable a specific handle using the `cdkDragHandleDisabled` input on a `cdkDragHandle` or an entire list using the `cdkDropListDisabled` input on a `cdkDropList`.

```
<div cdkDropList class="list" (cdkDropListDropped)="drop($event)">
  <div
    class="box"
    *ngFor="let item of items"
    cdkDrag
    [cdkDragDisabled]="item.disabled">{{item.value}}</div>
</div>
```

In our `app.component.ts` we set the boolean function of our value to either true/false. 

```
import {Component} from '@angular/core';
import {CdkDragDrop, moveItemInArray} from '@angular/cdk/drag-drop';
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  items = [
    {value: 'Oranges', disabled: false},
    {value: 'Bananas', disabled: true},
    {value: 'Mangoes', disabled: false},
  ];
  drop(event: CdkDragDrop<string[]>) {
    moveItemInArray(this.items, event.previousIndex, event.currentIndex);
  }
```

Our value 'Banana' is set `disabled:true`, which invokes the `cdkDragDisabled` to disable dragging for that particular item. Click [here](https://beta-angular-material-io.firebaseapp.com/cdk/drag-drop/overview#disabled-dragging) for more information.

![6](https://blog.openreplay.com/5cdaced9c27b96f9dc72f455a5381dff/image06.gif)

## Conclusion

Angular Material is flat and extremely simple by design. It is made with the understanding that adding new CSS rules is considerably simpler than changing already-existing CSS rules. This is simple and intuitive to use, but it also offers flexibility by allowing you to take charge when needed. 

You can get more information by consulting the [s](https://github.com/EJINEROZ/Angular-drag-n-drop)[ourcecode](https://github.com/EJINEROZ/Angular-drag-n-drop) and [documentation](https://material.angular.io/cdk/drag-drop/overview).

Important resources:

- [Angular Material](https://material.angular.io/)
- [Angular Drag & Drop](https://material.angular.io/cdk/drag-drop/overview)
- [Angular](https://angular.io/start)

[![newsletter](https://blog.openreplay.com/static/e6b18eda7a327511296a2f27692f9c70/e9957/newsletter.png)](https://newsletter.openreplay.com/)