AngularのDragDropModuleを使ってToDoリストを作る
AngularでTrelloのようなリストをドラッグ&ドロップできるような画面を作る方法をご紹介します!
Angular Material CDKのDragDropModuleを使用します。
検証環境
Angular CLI: 8.3.21 Node: 12.13.0 OS: darwin x64 Angular: 8.2.14 @angular/cdk: 8.2.3
事前準備
@angular/cdk
が入っていない場合は追加しましょう。
$ ng add @angular/cdk
Angularのプロジェクト作成方法はこの記事では割愛します。
実装してみる
app.module.tsにインポート文を追加
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { AppRoutingModule } from './app-routing.module'; import { DragDropModule } from '@angular/cdk/drag-drop'; import { AppComponent } from './app.component'; @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, AppRoutingModule, DragDropModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
DragDropModule
のインポート文を追加します。
ToDoリストを作る
ますはコンポーネントを作成しましょう。
$ ng g component component/todo
HTML
<div class="todo-container"> <h2>To do</h2> <div cdkDropList #todoList="cdkDropList" [cdkDropListData]="todo" [cdkDropListConnectedTo]="[doneList]" class="todo-list" (cdkDropListDropped)="drop($event)"> <div class="todo-box" *ngFor="let item of todo" cdkDrag>{{item}}</div> </div> </div> <div class="todo-container"> <h2>Done</h2> <div cdkDropList #doneList="cdkDropList" [cdkDropListData]="done" [cdkDropListConnectedTo]="[todoList]" class="todo-list" (cdkDropListDropped)="drop($event)"> <div class="todo-box" *ngFor="let item of done" cdkDrag>{{item}}</div> </div> </div>
- まず
cdkDropList
でドラッグ&ドロップを適用するリストを指定します。 - 2つのリスト間を移動できるよう、
cdkDropListConnectedTo
でお互いをドロップ先として指定します。 cdkDrag
ディレクティブを指定することで、その要素が移動できるようになります。cdkDropListDropped
でドロップ後にTypescript側の関数を呼び出します。
TS
import { Component, OnInit } from '@angular/core'; import { CdkDragDrop, moveItemInArray, transferArrayItem } from '@angular/cdk/drag-drop'; @Component({ selector: 'app-todo', templateUrl: './todo.component.html', styleUrls: ['./todo.component.scss'] }) export class TodoComponent implements OnInit { todo = [ 'おせちを食べる', 'お雑煮を食べる', 'おしるこを食べる', 'お年玉をあげる' ]; done = [ '年越しそばを食べる', 'カウントダウンをする' ]; constructor() {} ngOnInit() {} 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); } } }
dropに渡されたeventには移動した要素のドラッグ前のインデックス previousIndex
やドロップ後のインデックス currentIndex
が取得できます。
同じ配列内でインデックスを変更する場合は moveItemInArray
を呼び出します。
対して transferArrayItem
は別の配列に要素を移動する場合に呼び出します。
CSS
.todo-container { width: 400px; max-width: 100%; margin: 0 25px 25px 0; display: inline-block; vertical-align: top; } .todo-list { border: solid 1px #ccc; min-height: 60px; background: white; border-radius: 4px; overflow: hidden; display: block; } .todo-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: grab; 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); } .todo-box:last-child { border: none; } .todo-list.cdk-drop-list-dragging .todo-box:not(.cdk-drag-placeholder) { transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); }
実際のコードはこちらです。