1. はじめに
業務用システムなどの開発にあたって、一覧表示するデータの列や行を自由に動かしたいという要望を受けることがあると思いますが、なかなかに実装が大変です。本稿では、Angularを用いた実装のサンプルをご紹介します。
2. 列入れ替えの壁
Angularでは@angular/cdk/drag-drop
というモジュールを導入することで、リスト上に定義されているタグに対してドラッグ&ドロップを簡単に実装することができます。
このモジュールを用いて、以下のように実装することで行の入れ替えは容易に実現できます。
【参考】 Angular CDK Drag/Drop List inside a table (not Material Table) - Handle rows distorting width
:
:
<table>
<thead>
<tr>
<th>#</th>
<th>First</th>
<th>Last</th>
<th>Email</th>
<th>Address</th>
</tr>
</thead>
<tbody cdkDropList (cdkDropListDropped)="onDrop($event)">
<tr *ngFor="let user of users" cdkDrag cdkDragLockAxis="y">
<th>{{ user.order }}</th>
<td>{{ user.first }}</td>
<td>{{ user.last }}</td>
<td>{{ user.email }}</td>
<td>{{ user.address }}</td>
</tr>
</tbody>
</table>
:
:
onDrop関数の引数にはCdkDragDropが渡されますが、この中にドラッグ元のTRタグのインデックス番号とドラッグ先のTRタグのインデックス番号が含まれるため、そのインデックス番号をもとに、データの入れ替えが実現できます。
上述のコードを見ていただくとわかりますが、行データの入れ替えにあたって、対象のTRをそのままの形で移動すれば良いだけであり、シンプルに実装できます。
一方、列データの入れ替えはそう単純な話ではないです。なぜかというと、同一列のデータ(TDタグ)が複数のTRタグに分散して配置されるため、それぞれに対して操作が必要になるためです。なお、@angular/cdk/drag-drop
では、ドラッグ中に対象要素を持ち上げる視覚効果が入ります。その視覚効果に対しても考慮が必要となります。
3. 列入れ替え(単純実装版)
行の入れ替え同様の視覚効果を求めない場合、列入れ替えの実装は行同様に簡単に行なえます。
:
:
<table>
<thead>
<tr
cdkDropList
cdkDropListOrientation="horizontal"
(cdkDropListDropped)="onDropCol($event)"
>
<th cdkDrag>#</th>
<th cdkDrag>First</th>
<th cdkDrag>Last</th>
<th cdkDrag>Email</th>
<th cdkDrag>Address</th>
</tr>
</thead>
:
:
</table>
:
:
ドラッグ時の視覚効果は以下のようにタイトルのみとなります。
これでも良い場合は、以下は蛇足になります。
4. 列入れ替え(実装頑張った版)
ややこしいため詳細説明は割愛します。サンプル実装を参照してください。
なお、実装にあたっての気をつけなければならないポイントは以下になります。
- ドラッグ時の視覚効果はカスタマイズが可能であるためその機能を用いて列全体が持ち上げられているように見せる(Customizing the drag preview)
- ドラッグ時はBody閉じタグ付近に置かれる対象タグのコピーを操作していることになる。そのためEvent内に格納されているtargetの親を辿ってもTableタグにたどり着けないことに注意する。ドラッグしている要素のIndexを拾えるようにカスタム属性(サンプル実装ではdata-col-indexとして定義)を追加しておく。
- 列のドラッグ中の視覚効果をTBODY内の各対象TDタグに適用するために、
@angular/cdk/drag-drop
で定義するclassを付けたり、外したりする必要がある(Animations)
5. おわりに
もう少しきれいな実装ができればよかったのですが。。。