8

前言

写到表单功能的时候,就考虑如果表单能够拖动式地添加,无疑能够加强用户的使用体验感。所以就尝试着实现拖动功能。如下图,能够将一个表单拖动到另一个表单。
image.png

环境配置

使用到了angular的cdk:@angular/cdk/drag-drop

Angualr drag-drop里面的功能能让我们非常方便的处理页面上视图的拖拽(自由拖拽、列表排序拖拽、列表之间拖拽)问题。

官网:https://material.angular.io/c...

cdk是Angular Material 下的一个模块.我们安装一下Material。

安装material

ng add @angular/material

ng add命令将安装 Angular Material、 组件开发工具包 (CDK)

Module导入

import { DragDropModule } from '@angular/cdk/drag-drop';

imports: [

...

DragDropModule

]

功能实现

讲一下最常用的几种用法

拖拽

html代码:
最主要的就是加上了 cdkDrag

<div cdkDrag> drag me</div>

效果:
dragme.gif

排序

使用cdkDropList,它添加在一组元素添加cdkDrag可拖动元素的集合外面。随着元素的移动,项目将自动重新排列。

html:

<div class="list-group" cdkDropList 
(cdkDropListDropped)="drop($event)">
  <div class="list-group-item row" *ngFor="let customer of customers" cdkDrag>
    {{customer.name}}
  </div>
</div>

ts:

drop(event: CdkDragDrop<string[]>) {
    moveItemInArray(this.customers, event.previousIndex, event.currentIndex);
  }

效果图:
list1.gif





当然还可以横向排序,只需要添加cdkDropListOrientation="horizontal"

<div class="box-list-horizontal" cdkDropList 
cdkDropListOrientation="horizontal">

添加动画

看起来上面的效果图有些不太好看,我们可以给它添加一点动画。

css:

// 拖拽时显示的占位符元素,而不是实际的元素
.cdk-drag-placeholder {
  opacity: 0;
}

// 从动画的位置到最终把它放在列表的位置上时的动画
.cdk-drag-animating {
  transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
}

// 拖动元素时,看到的预览元素
.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);
}

// 拖动元素时,其他元素改变位置看到的动画
.list-group.cdk-drop-list-dragging .list-group-item:not(.cdk-drag-placeholder) {
  transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
}



效果图:

list.gif


还有其他的其他的一些功能将不再一一讲述,比如拖放位置锁定、设置拖放边界、拖放禁用功能。
可以看看官网:https://material.angular.io/c...

两个列表之间的拖拽

目前需要使用的是列表之间的拖拽,所以重点讲一下这个。

列表之间的连接

使用cdkDropListConnectedTo和id,可以连接两个表,使它们之间能够拖动自己的元素到另一个表中。

<div cdkDropList id="list-one" [cdkDropListConnectedTo]="['list-two']"></div>
<div cdkDropList id="list-two" [cdkDropListConnectedTo]="['list-one']"></div>



也可以使用cdkDropListGroup。cdkDropListGroup在下的所有表都将自动连接到所有其他列表。我采用的是这种。

<div cdkDropListGroup>
  <!-- All lists in here will be connected. -->
  <div cdkDropList *ngFor="let list of lists"></div>
</div>

根据需求写代码

首先,定义了一个表,需求是:不能排序,另一个不能拖动元素到此表单

  <div class="row">
    <div class="col-2">
      <h2>可用表单</h2>
      <div
        cdkDropList
        [cdkDropListData]="availableItems" // 拖动时所带的元素
        cdkDropListSortingDisabled // 禁用排序
        [cdkDropListEnterPredicate]= "noReturnPredicate" // 定义了一个函数,返回false, 表示别的表单不能拖动元素到此表单
        class="list-group">
        <div class="list-group-item" *ngFor="let item of availableItems" cdkDrag>
          <label> {{item.content}}</label>
        </div>
      </div>
    </div>
  </div>


定义了另一个表,需求是:可以排序,另一个表的元素拖动过来的时候,原来的元素不会消失。

<div class="col-6">
      <h2>目前表单</h2>
      <div
        cdkDropList
        class="list-group"
        [cdkDropListData]="nowItem"
        (cdkDropListDropped)="dropNowList($event)">
        <div class="list-group-item row" cdkDrag *ngFor="let item of nowItem">
          {{item.content}}
        </div>
      </div>
    </div>

效果图:

form.gif



ts:
当元素从左边的表拖动到右边的表的时候,或者右边的表排序的时候,右边的表会执行dropNowList()函数。函数中进行了判断,如代码中注释所示

/**
   * 现有表单
   * @param event
   */
  dropNowList(event: CdkDragDrop<FormItem[], any>) {
    // 如果前容器等于现容器,说明是在排序,交换元素的位置
    if (event.previousContainer === event.container) {
      moveItemInArray(event.container.data, event.previousIndex, event.currentIndex);
    } else {
    // 如果不同,说明是从另一个表过来的元素,获取元素前容器的数据,复制元素,插入到本表中
    // 如果使用transferItem(),则源元素会消失
      copyArrayItem(
        event.previousContainer.data,
        event.container.data,
        event.previousIndex,
        event.currentIndex)
    }
  }

遇到的问题

动态渲染

其实最开始想的是,元素一拖过去就动态渲染。但是,动态组件是一个整体,我们并不能拖动其中的某一个组件,只能拖动整体。如下效果图:

form2.gif



所以最后放弃了动态渲染,让用户拖动好了表单再点击预览按钮。

拖动效果

从一个表拖动元素到另一个表到时候,这个元素在源表中会消失,虽然拖动过去之后会回来,但是看起来逻辑并不好。

示例:

form1.gif

所以需要实现的是,拖动元素的时候,这个元素在源表单不动。

但似乎DragModule并没有属性或者函数可以让我们调用来解决这个问题。

在github有一场对这个问题的大型讨论:
https://github.com/angular/co...

但似乎实现起来有点麻烦。

这个问题目前还不影响功能的实现,待后期有时间的时候再解决。


weiweiyi
1k 声望123 粉丝