Angular Material provides groundbreaking modern UI components. Angular Material Design components will help us construct attractive UI and UX, consistent and functional web pages, and web applications while keeping modern web design principles like browser portability and compatibility.
Angular Drag and Drop Example
To create a Drag and drop functionality, use DragDropModule in the Angular Material library. To start with practical, follow the below steps.
- Install the Angular and Angular Material library.
- Import prebuilt themes and icons.
- Import API reference.
- Create a service and model files.
- Define the subscriber.
- Add HTML and CSS code.
- Transferring items between lists.
Let us create an Angular project. If you have Angular CLI, then you need to upgrade your CLI.
Step 1: Install the Angular project and Angular Material Library
ng new ang9drag
Now, go into the project folder and open the project inside the visual studio code.
cd ang9drag code .
Install the hammerjs using the following command.
npm install --save hammerjs
Hammer.js is an optional dependency and helps with touch support for a few of the material components.
Now, install Angular Material and Angular Animations using the following command.
npm install --save @angular/material @angular/animations @angular/cdk
Now, include hammerjs inside an angular.json file. You can find this file on the root of the angular project.
"scripts": [ "./node_modules/hammerjs/hammer.min.js" ]
Step 2: Import the pre-built theme and Material icons
Angular Material comes with some pre-built themes. These themes provide us with set off the colors and basic styling.
The main available themes are these: indigo-pink, deeppurple-amber, purple-green, and pink-bluegrey.
To import the theme in our project, we can add the following code to your global styles.css file. The file is inside the src folder.
@import '~@angular/material/prebuilt-themes/indigo-pink.css';
You can also have access to the Material Design icons and use these named icons with the <mat-icon> component.
To import them to your project, you can add this to the head section of your project’s root index.html file.
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
Now the final step is to import BrowserAnimationsModule inside the app.module.ts file.
// app.module.ts import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { AppComponent } from './app.component'; @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, BrowserAnimationsModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
Step 3: API reference for Angular CDK drag-drop
We need to import the DragDropModule inside the app.module.ts file.
// app.module.ts import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; 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 { }
CdkDropList
It is the container that wraps a set of draggable items.
Selector: [cdkDropList] cdk-drop-list
Exported as: cdkDropList
Methods
It has a drop() method. It drops an item into this container.
Parameters | |
---|---|
item
|
The item is dropped into the container. |
currentIndex
|
Index at which the item should be inserted. |
previousContainer
|
Container from which the item got dragged in. |
isPointerOverContainer
|
Whether the user’s pointer was over the container when the item was dropped. |
Step 4: Create a service and model files
Create an Angular service using the following command.
ng g s student --spec=false
It will create a file student.service.ts file inside the src >> app folder.
We have created a service because we will use the service to handle the data that needs to be displayed on the front end.
Also, create a new file inside the src >> app folder called student.model.ts and add the following code inside it.
// student.model.ts export class Student { name: String; }
Now, we need to add the demo data inside the student.service.ts file. The data is the type of Student model which we have defined above.
// student.service.ts import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { Student } from './student.model'; @Injectable({ providedIn: 'root' }) export class StudentService { students: Student[] = [ { name: 'Krunal' }, { name: 'Ankit' }, { name: 'Rushabh' }, { name: 'Dhaval' }, { name: 'Nehal' }, constructor() { } public getStudents(): any { const studentsObservable = new Observable(observer => { setTimeout(() => { observer.next(this.students); }, 1000); }); return studentsObservable; } }
So, here we have done is first import the Observable from rxjs. It then defined one function that will return an observable. Finally, the observable object gets one argument that has a timeout function.
So after 1 second, it will produce the whole student’s array if the subscriber subscribes to the observable.
In simple terms, here, studentObservable is publishing our primary data array, that is, students.
So if any entity needs to get the values out of observable, it first needs to subscribe to that observable. Then studentObservable starts to publish the values, and then subscribers get the values.
Step 5: Define the Subscriber
We have created the Publisher for the Observables. Now, we need to create a subscriber. So write the following code inside the app.component.ts file.
// app.component.ts import { Component, OnInit } from '@angular/core'; import {CdkDragDrop, moveItemInArray} from '@angular/cdk/drag-drop'; import { Student } from './student.model'; import { StudentService } from './student.service'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit { students: Student[] = []; constructor(private studentservice: StudentService) {} drop(event: CdkDragDrop<string[]>) { moveItemInArray(this.students, event.previousIndex, event.currentIndex); } ngOnInit() { const studentsObservable = this.studentservice.getStudents(); studentsObservable.subscribe((studentsData: Student[]) => { this.students = studentsData; }); } }
So, when the component Initializes, we will call the service method’s getStudents() methods and fetch all the student names in an array.
Step 6: Add CSS and HTML code
.example-list { width: 500px; max-width: 100%; border: solid 1px #ccc; min-height: 60px; display: block; background: white; border-radius: 4px; overflow: hidden; } .example-box { 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; } .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); } .example-box:last-child { border: none; } .example-list.cdk-drop-list-dragging .example-box:not(.cdk-drag-placeholder) { transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); }
It will help us to transform the HTML elements. Now, the final step is to add the HTML code inside the app.component.html file.
<!-- app.component.html --> <div cdkDropList class="example-list" (cdkDropListDropped)="drop($event)"> <div class="example-box" *ngFor="let student of students" cdkDrag>{{ student.name }}</div> </div>
Save the file and start the angular dev server.
ng serve --open
You will see something like this, and now you can drag and drop the items inside that box.
Step 7: Transferring items between lists
The cdkDropList
directive supports transferring dragged items between connected drop zones. You can connect one or more cdkDropList
instances by setting the property or by wrapping the elements in an element with the cdkDropListGroup
attribute.
Okay, now we will create the two sections, and we can interchange the lists between those two sections.
So, we need to create a second array called students2. We will define that array inside the app.component.ts file and do not go for services.
Write the following code inside the app.component.ts file.
// app.component.ts import { Component, OnInit } from '@angular/core'; import {CdkDragDrop, moveItemInArray, transferArrayItem} from '@angular/cdk/drag-drop'; import { Student } from './student.model'; import { StudentService } from './student.service'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit { students: Student[] = []; students2: Student[] = [ { name: 'Siddharth' }, { name: 'Jay' }, { name: 'Jaydeep' }, { name: 'Chirag' }]; constructor(private studentservice: StudentService) {} 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); } } ngOnInit() { const studentsObservable = this.studentservice.getStudents(); studentsObservable.subscribe((studentsData: Student[]) => { this.students = studentsData; }); } }
Also, we need to modify the app.component.html code.
<!-- app.component.html --> <div class="example-container"> <h2>Students</h2> <div cdkDropList #studentsList="cdkDropList" [cdkDropListData]="students" [cdkDropListConnectedTo]="[students2List]" class="example-list" (cdkDropListDropped)="drop($event)"> <div class="example-box" *ngFor="let student of students" cdkDrag>{{student.name}}</div> </div> </div> <div class="example-container"> <h2>Students 2</h2> <div cdkDropList #students2List="cdkDropList" [cdkDropListData]="students2" [cdkDropListConnectedTo]="[studentsList]" class="example-list" (cdkDropListDropped)="drop($event)"> <div class="example-box" *ngFor="let student2 of students2" cdkDrag>{{student2.name}}</div> </div> </div>
Save the file and go to the browser, and you will see something like this.
ERROR in src/app/app.component.ts(21,56): error TS2339: Property ‘getStudents’ does not exist on type ‘StudentService’.
src/app/student.service.ts(29,3): error TS2662: Cannot find name ‘constructor’. Did you mean the static member ‘StudentService.constructor’?
src/app/student.service.ts(31,3): error TS2304: Cannot find name ‘public’.
src/app/student.service.ts(31,10): error TS2304: Cannot find name ‘getStudents’.
src/app/student.service.ts(31,25): error TS2693: ‘any’ only refers to a type, but is being used as a value here.
src/app/student.service.ts(32,12): error TS2304: Cannot find name ‘studentsObservable’.
src/app/student.service.ts(38,13): error TS2304: Cannot find name ‘studentsObservable’.
Hi,
I need to move the item from one div to another.. but Div should not be cdkDropList. Random arrangemement of items inside div.
The student service you need to close the students array by putting a ] in the place of the ,