先看下问题展示吧

  • 问题描述:

    拖拽存放容器内带有拖拽存放容器,会产生当拖拽到最里层容器时候冒泡到外层容器中,导致两层容器重复获取数据
    问题展示

  • 问题展示
    • 蓝色区域
      -蓝色区域

    • 绿色区域

      • 绿色区域

安装使用angular-draggable-droppable

  • 安装
    • npm i angular-draggable-droppable
    • yarn add angular-draggable-droppable
  • 基本使用
    <div mwlDraggable [dropData]="{ data: 'this is a data!!!' }">Drag me!</div>
    <div mwlDroppable (drop)="onDrop($event)">
        <span [hidden]="droppedData">Drop here</span>
        <span [hidden]="!droppedData">
            Item dropped here with data: "{{ droppedData | json }}"!
        </span>
    </div>
    
    import { DragAndDropModule } from 'angular-draggable-droppable';
    
    imports: [DragAndDropModule],
    
    droppedData: any;
    onDrop(data){
        console.log(data)
        this.droppedData = data.dropData;
    }
    
  • 展示
    • 展示

问题代码

  • 目录结构
  • drag组件drop组件内拖入数据
    目录结构
  • Drag-Drop/drop/drop.component.html 存在两层拖拽目标容器
<div mwlDroppable
     (drop)="onDrop($event)">
    <pre>Main Data = {{mainData | json}}</pre>
    
    <div mwlDroppable
         (drop)="onDropPart($event)">
        <pre>Part Data = {{partData | json}}</pre>
    </div>
    
</div>
mainData: any = [];                             // 外层主容器
partData: any = [];                             // 里层副容器
/**
 * 主容器拖拽获取数据
 * @param data:数据
 */
onDrop(data: DropEvent<any>) {
    console.log('Main Data', data);
    this.mainData.push(data);
}

/**
 * 副容器拖拽获取数据
 * @param data:数据
 */
onDropPart(data: DropEvent<any>) {
    console.log('Part Data', data);
    this.partData.push(data);
}

解决方案

  • 添加pointerup事件进行组织冒泡行为, 请忽略这个方法的第二个参数
    <div mwlDroppable
         (drop)="onDrop($event)"
         (pointerup)="onPointerMain($event, 'Main Up')">
    
        <div>
            <pre>Main Data = {{mainData | json}}</pre>
        </div>
    
        <div mwlDroppable
             (drop)="onDropPart($event)"
             (pointerup)="onPointerPart($event, 'Part Up')">
            <pre>Part Data = {{partData | json}}</pre>
        </div>
    
    </div>
    
    /**
     * 主容器的拖拽事件监听
     * @param ev :事件
     * @param desc :描述
     */
    onPointerMain(ev: PointerEvent, desc: string) {
        console.log(desc, ev);
        ev.preventDefault();
        ev.stopPropagation();
    }
    
    /**
     * 副容器的拖拽事件监听
     * @param ev :事件
     * @param desc :描述
     */
    onPointerPart(ev: PointerEvent, desc: string) {
        console.log(desc, ev);
        ev.preventDefault();
        ev.stopPropagation();
    }
    
  • 测试以上代码后发现冒泡穿透事件已被阻止,但因为drop事件并未作出修改导致以上代码目前并没有卵用
  • 修改drop方法,定义一个临时存储拖拽数据的变量
    private tempData: DropEvent<any>;
    
    /**
     * 主容器拖拽获取数据
     * @param data:数据
     */
    onDrop(data: DropEvent<any>) {
        console.log('Main Data', data);
        this.tempData = data;
    }
    
    /**
     * 副容器拖拽获取数据
     * @param data:数据
     */
    onDropPart(data: DropEvent<any>) {
        console.log('Part Data', data);
        this.tempData = data;
    }
    
  • 然后发现这样修改后只是存了一下变量,但是还没有使用
  • 使用临时变量tempData
    /**
     * 主容器的拖拽事件监听
     * @param ev :事件
     * @param desc :描述
     */
    onPointerMain(ev: PointerEvent, desc: string) {
        console.log(desc, ev);
        ev.preventDefault();
        ev.stopPropagation();
        this.mainData.push(this.tempData);
    }
    /**
     * 副容器的拖拽事件监听
     * @param ev :事件
     * @param desc :描述
     */
    onPointerPart(ev: PointerEvent, desc: string) {
        console.log(desc, ev);
        ev.preventDefault();
        ev.stopPropagation();
        this.partData.push(this.tempData);
    }
    
  • 这时候发现添加的数据,第一次添加的是null,因为pointerup是监听鼠标抬起事件,当抬起鼠标时这一时刻tempData变量还并没有数据,所以是null
  • 解决:
    /**
     * 主容器的拖拽事件监听
     * @param ev :事件
     * @param desc :描述
     */
    onPointerMain(ev: PointerEvent, desc: string) {
        console.log(desc, ev);
        ev.preventDefault();
        ev.stopPropagation();
        setTimeout(_ => {
           this.mainData.push(this.tempData);
        });
    }
    /**
     * 副容器的拖拽事件监听
     * @param ev :事件
     * @param desc :描述
     */
    onPointerPart(ev: PointerEvent, desc: string) {
        console.log(desc, ev);
        ev.preventDefault();
        ev.stopPropagation();
        setTimeout(_ => {
           this.partData.push(this.tempData);
        });
    }
    
  • 这时候数据的冒泡穿透问题基本解决了,但是因为pointerup监听的是容器内鼠标的抬起事件,这时候不拖动数据每次只在容器内点击鼠标在抬起都会向容器内添加一条数据,所以应该在每次获取完成数据后将tempData内的数据清空,并根据tempData是否为空在判断是否向容器内添加数据
  • 修改完善:
    /**
     * 主容器的拖拽事件监听
     * @param ev :事件
     * @param desc :描述
     */
    onPointerMain(ev: PointerEvent, desc: string) {
        console.log(desc, ev);
        ev.preventDefault();
        ev.stopPropagation();
        setTimeout(_ => {
            if (this.tempData) {
                this.mainData.push(this.tempData);
                this.tempData = null;
            }
        });
    }
    /**
     * 副容器的拖拽事件监听
     * @param ev :事件
     * @param desc :描述
     */
    onPointerPart(ev: PointerEvent, desc: string) {
        console.log(desc, ev);
        ev.preventDefault();
        ev.stopPropagation();
        setTimeout(_ => {
            if (this.tempData) {
                this.partData.push(this.tempData);
                this.tempData = null;
            }
        });
    }
    

最终展示

最终展示

  • 注意:由于判断了临时数据的是空与否,这时候不去阻止冒泡也能实现功能,但是问题主要还是出现在因为冒泡的影响上,所以冒泡事件还是阻止比较好,这样也不会让监听抬起事件执行多次

完整代码

  • html
<!-- Drag-Drop/drag-drop.component.html -->
<h2>@ Drag Drop Data</h2>
<div nz-row [nzGutter]="[10, 10]">
    <div nz-col nzSpan="4">
        <div class="ant-alert-warning pad_10">
            <app-drag></app-drag>
        </div>
    </div>
    <div nz-col nzSpan="20">
        <div class="ant-alert-info min-h_400 pad_10">
            <app-drop></app-drop>
        </div>
    </div>
</div>

<!-- Drag-Drop/drag/drag.component.html -->
<div nz-row [nzGutter]="[10 ,10]">
    <ng-container *ngFor="let item of data; let i = index;">
        <div nz-col
             nzSpan="24"
             mwlDraggable
             [dropData]="item"
             class="z10">
            <button nz-button nzBlock nzSize="small" nzType="dashed">{{item.key}}</button>
        </div>
    </ng-container>
</div>

<!-- Drag-Drop/drop/drop.component.html -->
<div class="h-100 flex"
     mwlDroppable
     (drop)="onDrop($event)"
     (pointerup)="onPointerMain($event, 'Main Up')">

    <div class="flex1 pad_r_10">
        <nz-alert nzType="info" nzMessage="Main 请拖拽到此区域"></nz-alert>
        <pre class="pad_10">Main Data = {{mainData | json}}</pre>
    </div>

    <div class="flex1 ant-alert-success min-h_400 pad_10"
         mwlDroppable
         (drop)="onDropPart($event)"
         (pointerup)="onPointerPart($event, 'Part Up')">
        <nz-alert nzType="success" nzMessage="Part 请拖拽到此区域"></nz-alert>
        <pre class="pad_10">Part Data = {{partData | json}}</pre>
    </div>

</div>
  • css
/* Drag-Drop/drag-drop.component.css */
.min-h_400{
    min-height: 400px;
}
.pad_10{
    padding: 10px;
}
.pad_r_10{
    padding-right: 10px;
}
.h-100{
    height: 100%;
}

.z10{
    z-index: 10;
}
.flex{
    display: flex;
    justify-content: space-between;
    align-items: self-start;
}
.flex1{
    flex: 1;
}
pre{
    margin: 10px 0 0 0;
    background-color: #000;
    color: #fff;
}
  • ts
// Drag-Drop/drag/drag.component.ts
import {Component, OnInit} from '@angular/core';

@Component({
    selector: 'app-drag',
    templateUrl: './drag.component.html',
    styleUrls: ['../drag-drop.component.css']
})
export class DragComponent implements OnInit {
    data: any = [
        {
            key: 'Data 01'
        },
        {
            key: 'Data 02'
        }
    ];
    constructor() {
    }

    ngOnInit(): void {
    }
}

// Drag-Drop/drop/drop.component.ts
import {Component, OnInit} from '@angular/core';
import {DropEvent} from 'angular-draggable-droppable';

@Component({
    selector: 'app-drop',
    templateUrl: './drop.component.html',
    styleUrls: ['../drag-drop.component.css']
})
export class DropComponent implements OnInit {

    mainData: any = [];                             // 外层主容器
    partData: any = [];                             // 里层副容器
    private tempData: DropEvent<any>;
    
    constructor() {
    }

    ngOnInit(): void {
    }

    /**
     * 主容器拖拽获取数据
     * @param data:数据
     */
    onDrop(data: DropEvent<any>) {
        console.log('Main Data', data);
        this.tempData = data;
    }

    /**
     * 副容器拖拽获取数据
     * @param data:数据
     */
    onDropPart(data: DropEvent<any>) {
        console.log('Part Data', data);
        this.tempData = data;
    }
    
    /**
     * 主容器的拖拽事件监听
     * @param ev :事件
     * @param desc :描述
     */
    onPointerMain(ev: PointerEvent, desc: string) {
        console.log(desc, ev);
        ev.preventDefault();
        ev.stopPropagation();
        setTimeout(_ => {
            if (this.tempData) {
                this.mainData.push(this.tempData);
                this.tempData = null;
            }
        });
    }
    
    /**
     * 副容器的拖拽事件监听
     * @param ev :事件
     * @param desc :描述
     */
    onPointerPart(ev: PointerEvent, desc: string) {
        console.log(desc, ev);
        ev.preventDefault();
        ev.stopPropagation();
        setTimeout(_ => {
            if (this.tempData) {
                this.partData.push(this.tempData);
                this.tempData = null;
            }
        });
    }
    
}
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐