Angular 自定义拖拽指令
指令组件是一种带模版的指令。指令是超级。 结构型指令(改变布局)和属性型指令(改变外观和行为)。 Renderer2和ElementRef Angular不提倡直接操作DOM 对于DOM的操作应该通过Renderer2进行。 ElementRef是指向DOM元素的引用 拖拽指令实例1、新建directive module$ ng g m directive
CREATE src/app/directive/directive.module.ts (193 bytes)
2,在指令文件夹下的drag-drop文件夹里新建一个drag和一个drop$ ng g d directive/drag-drop/drag
$ ng g d directive/drag-drop/drop
3,改一下selector
selector: ‘[appDrag]‘改为
selector: ‘[app-draggable]‘
selector: ‘[appDrop]‘改为
selector: ‘[app-droppable]‘
4,在SharedModule中导入DirectiveModuleimport { NgModule } from "@angular/core"; import { CommonModule } from "@angular/common"; import { MaterialModule } from "../material/material.module"; import { ConfirmDialogComponent } from "./confirm-dialog/confirm-dialog.component"; import { DirectiveModule } from ‘../directive/directive.module‘; @NgModule({ imports: [CommonModule,MaterialModule,DirectiveModule],exports: [CommonModule,declarations: [ConfirmDialogComponent],entryComponents: [ConfirmDialogComponent] }) export class SharedModule { } 5,把DirectiveModule中多余的CommonDodule删掉,然后把Drag和Drop两个指令导出import { NgModule } from ‘@angular/core‘; import { DragDirective } from ‘./drag-drop/drag.directive‘; import { DropDirective } from ‘./drag-drop/drop.directive‘; @NgModule({ declarations: [DragDirective,DropDirective],exports: [DragDirective,}) export class DirectiveModule { } ? 6,drag指令使用@HostListener监听dragstart事件和dragend事件。 使用ElementRef获取元素。 使用Renderer2修改样式。 draggedClass是一个输入型参数。所以selector 为selector: ‘[app-draggable][draggedClass]‘。 app-draggable设置为true就可以拖拽,为false不可拖拽。 private _isDraggble = false; set isDraggable(value){ this._isDraggble=value; } get isDraggable(){ return this._isDraggble; } 给set方法加上@Input(app-draggable)。 这样在调用app-draggable= "true"的时候会调用set方法。 import { Directive,HostListener,Host,ElementRef,Renderer2,Input } from ‘@angular/core‘; @Directive({ selector: ‘[app-draggable][draggedClass]‘ }) export class DragDirective { private _isDraggble = false; @Input(‘app-draggable‘) set isDraggable(value:boolean){ this._isDraggble=value; this.rd.setAttribute(this.el.nativeElement,‘draggable‘,`${value}`); } get isDraggable(){ return this._isDraggble; } @Input() draggedClass:string; constructor(private el:ElementRef,private rd:Renderer2) { } @HostListener(‘dragstart‘,[‘$event‘]) ondragstart(ev:Event){ //判断drag元素是不是指令应用的元素发起的 if(this.el.nativeElement===ev.target){ this.rd.addClass(this.el.nativeElement,this.draggedClass);//往el上增加一个class } } @HostListener(‘dragend‘,[‘$event‘]) ondragend(ev:Event){ if(this.el.nativeElement===ev.target){ this.rd.removeClass(this.el.nativeElement,this.draggedClass); } } } 在task-item组件中使用 //红色dash虚线半透明 <mat-list-item class="container" [@item]="widerPriority" [ngClass]="{ ‘priority-normal‘:item.priority===3,‘priority-important‘:item.priority===2,‘priority-emergency‘:item.priority===1 }" [app-draggable]="true" [draggedClass]=" ‘drag-start‘ " (click)="onItemClick()"> ...... </mat-list-item> 7,drop指令drop要监听dragenter,dragover,dragleave,drop四个事件。 需要改变自己的css。即拖放区域的css。变暗。 import { Directive,Input } from ‘@angular/core‘; @Directive({ selector: ‘[app-droppable][dragEnterClass]‘ }) export class DropDirective { @Input() dragEnterClass:string; constructor(private el:ElementRef,private rd:Renderer2) { } @HostListener(‘dragenter‘,[‘$event‘]) onDragEnter(ev:Event){ //判断drag元素是不是指令应用的元素发起的 if(this.el.nativeElement===ev.target){ this.rd.addClass(this.el.nativeElement,this.dragEnterClass);//往el上增加一个class } } @HostListener(‘dragover‘,[‘$event‘]) onDragOver(ev:Event){ //判断drag元素是不是指令应用的元素发起的 if(this.el.nativeElement===ev.target){ } } @HostListener(‘dragleave‘,[‘$event‘]) onDragLeave(ev:Event){ //判断drag元素是不是指令应用的元素发起的 if(this.el.nativeElement===ev.target){ this.rd.removeClass(this.el.nativeElement,this.dragEnterClass);//往el上增加一个class } } @HostListener(‘drop‘,[‘$event‘]) onDrop(ev:Event){ //判断drag元素是不是指令应用的元素发起的 if(this.el.nativeElement===ev.target){ this.rd.removeClass(this.el.nativeElement,this.dragEnterClass);//往el上增加一个class } } } 在task-home中使用drop指令 .drag-enter{ background-color: dimgray; } <app-task-list *ngFor="let list of lists" class="list-container" app-droppable="true" [dragEnterClass]=" ‘drag-enter‘ " > ...... </app-task-list> ?至此,问题是不能多重拖拽(list-item和list都能拖拽,区分不开。)和携带数据。 8、解决多重拖拽和携带数据的问题创建一个service $ ng g s directive/drag-drop 在DirectiveModule中providers里声明一下 import { NgModule } from ‘@angular/core‘; import { DragDirective } from ‘./drag-drop/drag.directive‘; import { DropDirective } from ‘./drag-drop/drop.directive‘; import { DragDropService } from ‘./drag-drop.service‘; @NgModule({ declarations: [DragDirective, providers:[DragDropService] }) export class DirectiveModule { } drag-drop.service.ts import { Injectable } from ‘@angular/core‘; import { Observable,BehaviorSubject } from ‘rxjs‘; //数据结构 export interface DragData{ tag:string; //多重拖拽的话是哪一级拖拽,用户自己保证唯一性,不能重复 data:any; //传递的数据 } @Injectable({ providedIn: ‘root‘ }) export class DragDropService { //用BehaviorSubject总能记住上一次的值 private _dragData = new BehaviorSubject<DragData>(null); setDragData(data:DragData){ this._dragData.next(data); } getDragData():Observable<DragData>{ return this._dragData.asObservable(); } clearDragData(){ this._dragData.next(null); } constructor() { } } 9,更新drag在drag的时候需要多定义一个属性dragTag, constructor中注入新的DragDropService, 在开始拖拽的时候就给service set上数据,这里需要多定义一个dragData。import { Directive,Input } from ‘@angular/core‘;import { DragDropService } from ‘../drag-drop.service‘; @Directive({ selector: ‘[app-draggable][dragTag][dragData][draggedClass]‘ }) export class DragDirective { private _isDraggble = false; @Input(‘app-draggable‘) set isDraggable(value:boolean){ this._isDraggble=value; this.rd.setAttribute(this.el.nativeElement,`${value}`); } get isDraggable(){ return this._isDraggble; } @Input() draggedClass:string; ? 10,更新drop对于drop来讲,它的tags是一个数组,而不是字符串了。 因为拖放放的区域,可能会支持多个拖的区域。 所以放的时候原来的判断都有问题,首先需要判断这个拖拽是不是你能够接收的。 建立一个私有的data$,在constructor里订阅data。 drop指令还需要一个Output,因为需要什么时候drop。 import { Directive,Input,Output,EventEmitter } from ‘@angular/core‘; import { DragDropService,DragData } from ‘../drag-drop.service‘; import { take } from ‘rxjs/operators‘; @Directive({ selector: ‘[app-droppable][dropTags][dragEnterClass]‘ }) export class DropDirective { @Output() dropped = new EventEmitter<DragData>(); @Input() dragEnterClass:string; @Input() dropTags:string[] = []; private data$; constructor( private el:ElementRef,private service:DragDropService) { this.data$ = this.service.getDragData().pipe( take(1)); } // @HostListener(‘dragenter‘,[‘$event‘]) // onDragEnter(ev:Event){ // //判断drag元素是不是指令应用的元素发起的 // if(this.el.nativeElement===ev.target){ // this.rd.addClass(this.el.nativeElement,this.dragEnterClass);//往el上增加一个class // } // } @HostListener(‘dragenter‘,[‘$event‘]) onDragEnter(ev:Event){ //判断drag元素是不是指令应用的元素发起的 if(this.el.nativeElement===ev.target){ this.data$.subscribe(dragData=>{ if(this.dropTags.indexOf(dragData.tag)>-1){ this.rd.addClass(this.el.nativeElement,this.dragEnterClass);//往el上增加一个class } }); } } // @HostListener(‘dragover‘,[‘$event‘]) // onDragOver(ev:Event){ // //判断drag元素是不是指令应用的元素发起的 // if(this.el.nativeElement===ev.target){ // } // } //dragover允许进行data transfer的一些特效 @HostListener(‘dragover‘,[‘$event‘]) onDragOver(ev:Event){ //需要支持多级拖拽,所以要防止事件冒泡 ev.preventDefault(); ev.stopPropagation(); //判断drag元素是不是指令应用的元素发起的 if(this.el.nativeElement===ev.target){ this.data$.subscribe(dragData=>{ if(this.dropTags.indexOf(dragData.tag)>-1){ this.rd.setProperty(ev,‘dataTransfer.effectAllowed‘,‘all‘); this.rd.setProperty(ev,‘dataTransfer.fropEffect‘,‘move‘); }else{ this.rd.setProperty(ev,‘none‘); this.rd.setProperty(ev,‘dataTransfer.dropEffect‘,‘none‘); } }); } } @HostListener(‘dragleave‘,[‘$event‘]) onDragLeave(ev:Event){ ev.preventDefault(); ev.stopPropagation(); //判断drag元素是不是指令应用的元素发起的 if(this.el.nativeElement===ev.target){ this.data$.subscribe(dragData=>{ if(this.dropTags.indexOf(dragData.tag)>-1){ this.rd.removeClass(this.el.nativeElement,this.dragEnterClass);//往el上增加一个class } }); } } @HostListener(‘drop‘,[‘$event‘]) onDrop(ev:Event){ ev.preventDefault(); ev.stopPropagation(); //判断drag元素是不是指令应用的元素发起的 if(this.el.nativeElement===ev.target){ this.data$.subscribe(dragData => { if(this.dropTags.indexOf(dragData.tag)>-1){ this.rd.removeClass(this.el.nativeElement,this.dragEnterClass);//往el上增加一个class this.dropped.emit(dragData);//drop的时候把dragData发射出去 this.service.clearDragData(); //drop的时候把data clear掉,否则会影响下一次拖拽 } }); } } } ? ? 11,改造模版调用对于taskItem <mat-list-item class="container" [@item]="widerPriority" [ngClass]="{ ‘priority-normal‘:item.priority===3,‘priority-emergency‘:item.priority===1 }" [app-draggable]= "true" [dragTag]= "‘task-item‘" [draggedClass]=" ‘drag-start‘ " [dragData]="item" (click)= "onItemClick()"> 对于taskHome 既能drag又能drop 此外还要处理一个dropped事件 <div class="task-list"> <app-task-list *ngFor="let list of lists" class="list-container" app-droppable="true" [dropTags]="[‘task-item‘,‘task-list‘]" [dragEnterClass]=" ‘drag-enter‘ " [app-draggable]="true" [dragTag]=" ‘task-list‘ " [draggedClass]=" ‘drag-start‘ " [dragData]="list" (dropped)="handleMove($event,list)" > handleMove(srcData,List){ switch (srcData.tag) { case ‘task-item‘: console.log(‘handling item‘); break; case ‘task-list‘: console.log(‘handling list‘); default: break; } } ?最终效果: (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |