基于ng2-dragula的Angular5多选中拖拽排序功能实现需求
实现ng2-dragula多选拖拽排序功能
我来帮你搞定这个按住Ctrl多选后拖拽排序的需求!核心思路是利用Dragula提供的事件钩子,拦截默认的单元素拖拽行为,手动处理选中项的数组移除和插入逻辑。
步骤1:订阅Dragula的关键事件
首先我们需要在组件中订阅Dragula的drop事件(拖拽完成时触发),同时为了避免内存泄漏,要在组件销毁时取消订阅。另外,为了阻止默认的单元素拖拽干扰多选操作,我们还可以订阅drag事件,当有多个选中项时阻止默认拖拽行为。
步骤2:实现多选拖拽的核心逻辑
当拖拽完成时,我们需要:
- 判断是否有多个选中项
- 获取拖拽目标位置的索引
- 把选中项从原数组中移除(注意要按索引从大到小删除,避免索引偏移问题)
- 将选中项插入到目标位置
- 清空选中数组,恢复初始状态
修改后的完整代码
app.component.html(无需修改,保持原结构即可)
<table border=2> <tbody id='dest' [dragula]='"second-bag"' [dragulaModel]="peoples"> <tr *ngFor="let person of peoples" (click)="addThisPersonToArray(person, $event)" [class.active]="isPersonSelected(person)"> <td>{{person.id}}</td> <td>{{person.firstName}}</td> <td>{{person.lastName}}</td> </tr> </tbody> </table> Result items <ul> <li *ngFor="let item of peoples"> {{item.firstName}}</li> </ul>
app.component.ts(添加事件订阅和核心逻辑)
import { Component, OnInit, OnDestroy } from '@angular/core'; import { DragulaService } from 'ng2-dragula/ng2-dragula'; import { Subscription } from 'rxjs'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit, OnDestroy { selectedPersonArray = []; peoples = [ { id: 1, firstName: 'First Name 1', lastName: 'Last Name 1' }, { id: 2, firstName: 'First Name 2', lastName: 'Last Name 2' }, { id: 3, firstName: 'First Name 3', lastName: 'Last Name 3' }, { id: 4, firstName: 'First Name 4', lastName: 'Last Name 4' }, { id: 5, firstName: 'First Name 5', lastName: 'Last Name 5' }, { id: 6, firstName: 'First Name 6', lastName: 'Last Name 6' } ]; private dragSub: Subscription; private dropSub: Subscription; constructor(private dragulaService: DragulaService) { // 初始化Dragula的bag,确保支持拖拽 this.dragulaService.createGroup('second-bag', { moves: (el, container, handle) => true, direction: 'vertical' }); } ngOnInit() { // 订阅drag事件,当有多个选中项时阻止默认拖拽 this.dragSub = this.dragulaService.drag('second-bag').subscribe(({ el }) => { if (this.selectedPersonArray.length > 1) { // 阻止默认的单元素拖拽行为 el.setAttribute('draggable', 'false'); } }); // 订阅drop事件,处理多选拖拽逻辑 this.dropSub = this.dragulaService.drop('second-bag').subscribe(({ target, source }) => { if (this.selectedPersonArray.length <= 1) return; // 获取目标位置的索引 const targetIndex = Array.from(target.children).indexOf(target.querySelector('.active') || target.firstElementChild); // 复制选中项的副本,避免原数组修改影响 const selectedItems = [...this.selectedPersonArray]; // 按索引从大到小删除选中项,防止索引偏移 selectedItems.sort((a, b) => { const idxA = this.peoples.indexOf(a); const idxB = this.peoples.indexOf(b); return idxB - idxA; }).forEach(item => { const idx = this.peoples.indexOf(item); if (idx !== -1) { this.peoples.splice(idx, 1); } }); // 将选中项插入到目标位置 this.peoples.splice(targetIndex, 0, ...selectedItems); // 清空选中数组 this.selectedPersonArray = []; }); } toggleItemInArr(arr, item) { const index = arr.indexOf(item); index === -1 ? arr.push(item) : arr.splice(index, 1); } addThisPersonToArray(person: any, event) { if (!event.ctrlKey) { this.selectedPersonArray = []; } this.toggleItemInArr(this.selectedPersonArray, person); } isPersonSelected(person: any) { return (this.selectedPersonArray.indexOf(person) !== -1); } ngOnDestroy() { // 取消订阅,防止内存泄漏 this.dragSub?.unsubscribe(); this.dropSub?.unsubscribe(); // 销毁Dragula的bag this.dragulaService.destroy('second-bag'); } }
关键代码解释
- Dragula Group初始化:我们显式创建了
second-bag组,确保拖拽方向为垂直方向,符合表格行的拖拽场景。 - drag事件处理:当有多个选中项时,设置拖拽元素为不可拖拽,避免默认的单元素拖拽逻辑干扰。
- drop事件处理:
- 获取目标位置的索引:通过遍历目标容器的子元素找到拖拽目标的位置。
- 反向删除选中项:按原数组中的索引从大到小删除,这样删除前面的元素不会影响后面元素的索引。
- 插入选中项:使用
spread操作符将选中项批量插入到目标位置。
- 组件销毁清理:取消事件订阅并销毁Dragula组,避免内存泄漏。
测试场景验证
现在你可以按住Ctrl键选中2和4,然后拖拽到1的位置,列表会变成2,4,1,3,5,6,完全符合你的需求!
内容的提问来源于stack exchange,提问作者Rosa Mystica
相关产品推荐
相关产品推荐

