In this Angular 9 tutorial, we’ll discuss how to implement drag and drop functionality using Angular Material library components. In the drag and drop lists, items can be dropped across multiple lists or in the same list.
In the Angular Material version 7, two major updates were introduced, Virtual Scroll, and Drag and Drop. Both these features prove very useful and productive from a user perspective. In this post, we will create an application to demonstrate the Drag and Drop functionality using the CDK’s DragDropModule
in Material 9.
The Drag and Drop feature enables a user to move anything from one place to another in a web application with the help of a mouse, this helps in creating an interactive, highly simple clean user interface. In our application we will have three user lists i.e Still Doing, Done and Nevermind. A user can drop an item from any list and can drop to any other list.
After completing our application will look like this
Let’s start …
Also, check post on Angular Material 7 Virtual Scroll Example
# Setup Angular CLI
We’ll create Angula project in the latest version. Make sure you have updated the Angular CLI tool by running below npm
command in the terminal
$ npm install -g @angular/cli</pre> <h3># Create an Angular Project</h3> Execute below <code>ng
command to create an Angular project in latest version 9.1.3. But this tutorial is compatible with previous version 7,6,5 and 4$ ng new angular-material-drag-and-drop-lists ? Would you like to add Angular routing? No ? Which stylesheet format would you like to use? SCSS</pre> Move to the project directory <pre class="wp-block-prismatic-blocks"><code class="language-javascript">$ cd angular-material-drag-and-drop-lists</pre> If you have Visual Studio Code( VS Code ) installed, run following to open in the project in VS Code <pre class="wp-block-prismatic-blocks"><code class="language-javascript">$ code .</pre> Open application server on the browser. <pre class="wp-block-prismatic-blocks"><code class="language-javascript">$ ng serve --open</pre> <h4></h4> <h3># Install Angular CDK Package</h3> The Drag and Drop components are available in the Component Dev Kit (CDK) package. Components are available in the CDK work as the core functionalities available in the Material UI library without any Material design. Here also we will use the Bootstrap CSS style for our Drag and Drop lists. Run following npm command to instal the CDK package in the project to use its modules. <pre class="wp-block-prismatic-blocks"><code class="language-javascript">$ npm install @angular/cdk</pre> <h3># Update App Module with <code>DragDropModule
The CDK package provides number of functionalities. Here we need to import theDragDropModule
to use its functionality. Open the app.module.ts file, import theDragDropModule
then update the modules array as shown below:// app.module.ts import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { AppComponent } from './app.component'; import { DragDropModule } from '@angular/cdk/drag-drop'; @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, DragDropModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }# Create a Draggable List
A draggable wrapper with items to be sortable is created by adding the
cdkDropList.
The Items in the list is created dynamically by defining the data Object in the[cdkDropListData].
If we want to multiple lists with items to be draggable or droppable inter between then we also add the
[cdkDropListConnectedTo]
property as well.<div class="drag-container"> <div class="section-heading">Still Doing</div> <div cdkDropList #pendingList="cdkDropList" [cdkDropListData]="todo" [cdkDropListConnectedTo]="[doneList,reviewList]" class="item-list" (cdkDropListDropped)="drop($event)"> <div class="item-box" *ngFor="let item of todo" cdkDrag>{{item}}</div> </div> </div></pre> Above is the list of tasks for <strong>Still Doing. </strong> <code><strong>cdkDropList</strong>
directive as a droppable list<strong>#pendingList</strong>
is list identifier<strong>cdkDropListData</strong>
takes list object<strong>cdkDropListConnectedTo</strong>
tells about, to which list it can communicate or drag and drop lists. We can add any number of lists, it takes an array of identifier list names.<strong>cdkDropListDropped</strong>
binds and event, fired when an item is dropped in this list.# Multiple DragDrop Lists
As a part of our application following is the final template to be placed in the app.component.html file. There are thee lists in which items can drag and drop to any list in between. The three task lists are Still Doing, Done and Nevermind. These are connected to each other by defining the array of the list defines in the[cdkDropListConnectedTo]
property.<div class="container"> <div class="row"> <div class="col-md-4"> <div class="drag-container"> <div class="section-heading">Still Doing</div> <div cdkDropList #pendingList="cdkDropList" [cdkDropListData]="todo" [cdkDropListConnectedTo]="[doneList,reviewList]" class="item-list" (cdkDropListDropped)="drop($event)"> <div class="item-box" *ngFor="let item of todo" cdkDrag>{{item}}</div> </div> </div> </div> <div class="col-md-4"> <div class="drag-container"> <div class="section-heading">Done</div> <div cdkDropList #doneList="cdkDropList" [cdkDropListData]="done" [cdkDropListConnectedTo]="[pendingList,reviewList]" class="item-list" (cdkDropListDropped)="drop($event)"> <div class="item-box" *ngFor="let item of done" cdkDrag>{{item}}</div> </div> </div> </div> <div class="col-md-4"> <div class="drag-container"> <div class="section-heading">Nevermind</div> <div cdkDropList #reviewList="cdkDropList" [cdkDropListData]="review" [cdkDropListConnectedTo]="[doneList,pendingList]" class="item-list" (cdkDropListDropped)="drop($event)"> <div class="item-box" *ngFor="let item of review" cdkDrag>{{item}}</div> </div> </div> </div> </div> </div> </pre> To make an item draggable, the <code>cdkDrag
directive is added. The drop event handler is defined in the(cdkDropListDropped)
 which is triggered when the item is placed in a list. It returns the following information about the previous and new container index number.# Update App Class with List Data and Drop Method
In app.components.ts we will define list objects and Droppable Event to arrange items in a list after a new item is added or removed from another list.import { Component } from '@angular/core'; import {CdkDragDrop, moveItemInArray, transferArrayItem} from '@angular/cdk/drag-drop'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'] }) export class AppComponent { todo = [ 'Get to work', 'Pick up groceries', 'Go home', 'Fall asleep' ]; done = [ 'Get up', 'Brush teeth', 'Take a shower', 'Check e-mail', 'Walk dog' ]; review = [ 'Take bath', 'Wash car', ]; drop(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); } } } </pre> Above we have defined the array for items in <code>todo, done
andreview
arrays. Thedrop()
method is handling the sorting and drop work by using themoveItemInArray()
andtransferArrayItem()
methods available in the'@angular/cdk/drag-drop'
package.# Styling the lists
To style our DragDrop lists we used Bootstrap CSS, just add css file in the index.html file's head section<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" crossorigin="anonymous"></pre> Also, update the <strong>app.component.scss</strong> file, add the following style to make it look pretty <pre class="lang:sass decode:true">.section-heading { padding: 5px 10px; font-size: 15px; font-weight: bold; } .drag-container { width: 400px; max-width: 100%; margin: 0 25px 25px 0; display: inline-block; vertical-align: top; background-color: #E9ECEF; padding: 15px; border-radius: 5px; } .item-list { min-height: 60px; border-radius: 4px; display: block; } .item-box { padding: 8px 10px; border: solid 1px #ccc; margin-bottom: 5px; 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; border-radius: 20px; } .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); border-radius: 20px; } .cdk-drag-placeholder { opacity: 0; } .cdk-drag-animating { transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); } .item-list.cdk-drop-list-dragging .item-box:not(.cdk-drag-placeholder) { transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); }</pre> That's all, now you will be able to see the working application having three lists, from which you can drag any item to drop back in same or another list. <h3># Bonus Tip</h3> <h3># Dynamic Inter Draggable & Droppable Lists</h3> Sometimes we may need lists dynamically generated, in that case, we can use the <code><strong>id</strong>
in place of # template variables as shown below.<div class="col-md-3" *ngFor="let week of weeks"> <div class="drag-container"> <div class="section-heading">Week {{week.id}}</div> <div cdkDropList id="{{week.id}}" [cdkDropListData]="week.weeklist" [cdkDropListConnectedTo]="connectedTo" class="item-list" (cdkDropListDropped)="drop($event)"> <div class="item-box" *ngFor="let weekItem of week.weeklist" cdkDrag>Week {{week.id}} {{weekItem}}</div> </div> </div> </div></pre> In this case, we have a dynamic JSON object <code>weeks
of multiple lists item as shown below. TheconnectedTo
array will have the list identifiers which are connected to each other.//app.component.ts import { Component } from '@angular/core'; import {CdkDragDrop, moveItemInArray, transferArrayItem} from '@angular/cdk/drag-drop'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { weeks = []; connectedTo = []; constructor(){ this.weeks = [ { id:'week-1', weeklist:[ "item 1", "item 2", "item 3", "item 4", "item 5" ] },{ id:'week-2', weeklist:[ "item 1", "item 2", "item 3", "item 4", "item 5" ] },{ id:'week-3', weeklist:[ "item 1", "item 2", "item 3", "item 4", "item 5" ] },{ id:'week-4', weeklist:[ "item 1", "item 2", "item 3", "item 4", "item 5" ] }, ]; for (let week of this.weeks) { this.connectedTo.push(week.id); }; } drop(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); } } }
Check the working demo link.
Also, check post on Angular Material 7 Virtual Scroll Example
Get source code in the GitHub Repo here.
Conclusion: Here we discussed how to make lists with drag and drop functionality using the CDK library. We can easily customize the functionality and styling due to its flexible configurations available.
Category: Angular
Leave a Reply