注:代码已上传到github,仅作为demo进行演示并学习,没有进行进一步开发,如有问题请随时联系我
github demo地址:https://github.com/Mr-WangZhe/DirectiveDemo
创建directive的moduleng g m directive
在module下,创建drag的directiveng g d directive/drag --spec=false
在module下,创建drop的directiveng g d directive/drop --spec=false
将drag.directive.ts和drop.directive.ts修改指令名称为[app-draggable]
和[app-droppable]
编写drag实现拖的目的
拖拽指令文件drag.directive.ts实例代码
import { Directive, HostListener, Input, ElementRef, Renderer2 } from '@angular/core';
import { DragDropService } from './drag-drop.service';
@Directive({
selector: '[app-draggable][dragTag][dragData][draggedClass]'
})
export class DragDirective {
private _isDraggable = false;//设置是否可拖拽的变量值
//定义属性方法(set) 使用的时候可以直接写成this.isDraggable=xxx;就会直接调用set方法
@Input('app-draggable')//定义app-draggable=xxx时就会调用set方法
set isDraggable(val) {
this._isDraggable = val;
this.rd.setAttribute(this.el.nativeElement,'draggable',`${val}`);//根据html规范,如果元素可拖拽或是不可拖拽,需要设置draggable属性值
}
//定义属性方法(get)
get isDraggable() {
return this._isDraggable;
}
@Input() dragTag:string;//tag的唯一标识
@Input() dragData:any;
@Input() draggedClass: string;//表示动态添加的样式
constructor(
private el:ElementRef,
private rd:Renderer2,
private service: DragDropService) {}
//通过event的target判断是否是指令应用的元素发起的
@HostListener('dragstart',['$event'])//监听拖开始的事件
onDragStart(ev:Event) {
if(this.el.nativeElement === ev.target) {
this.rd.addClass(this.el.nativeElement, this.draggedClass)//表示往el节点上应用draggedClass样式
this.service.setDragData({tag:this.dragTag,data:this.dragData})
}
}
@HostListener('dragend',['$event'])//监听拖结束的事件
onDragEnd(ev:Event) {
if(this.el.nativeElement === ev.target) {
this.rd.removeClass(this.el.nativeElement, this.draggedClass)//表示拖拽完成之后把样式移除
}
}
}
directive.module.ts文件定义
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { DragDirective } from './drag.directive';
import { DropDirective } from './drop.directive';
import { DragDropService } from './drag-drop.service';
@NgModule({
imports: [
CommonModule
],
declarations: [
DragDirective,
DropDirective
],
exports: [
DragDirective,
DropDirective
]
})
export class DirectiveModule { }
left-div.component.css定义拖拽时的css
.item-drag {
border: coral dashed 3px;
opacity: 0.5;
}
left-div.component.html定义显示的html
<div class="leftDivBackground">
<div *ngFor="let item of items; let i = index"
class="item"
[app-draggable]="true"
[draggedClass]="'item-drag'"
[dragTag]="'div-item'"
[dragData]="item">
{{item.value}}
</div>
</div>
编写drop实现放的目的
放置指令文件drop.directive.ts实例代码
import { Directive, HostListener, ElementRef, Renderer2, Input, Output, EventEmitter } from '@angular/core';
import { DragData, DragDropService } from './drag-drop.service';
import { take } from 'rxjs/operators';
@Directive({
selector: '[app-droppable][dragEnterClass]'
})
export class DropDirective {
@Output() dropped = new EventEmitter<DragData>();
@Input() dragEnterClass:string;
@Input() dropTags:string[] = [];//放的区域可能是多个区域,所以应该是数组
private data$;
constructor(
private el:ElementRef,
private rd:Renderer2,
private service: DragDropService) {
this.data$ = this.service.getDragData().pipe(take(1));//订阅service,需要导入take,take是rxjs的操作符
}
//通过event的target判断是否是指令应用的元素发起的
@HostListener('dragenter',['$event'])//drag的对象进入我的领域了
onDragEnter(ev:Event) {
// 多种元素都可以拖拽的话,拖拽的时候可能会影响多个,所以需要防止事件传播
ev.preventDefault();
ev.stopPropagation();
if(this.el.nativeElement === ev.target) {
this.data$.subscribe(dragData => {//取到data的时候,查看放的区域是否含有拖的tag
if(this.dropTags.indexOf(dragData.tag) > -1) {
this.rd.addClass(this.el.nativeElement, this.dragEnterClass)//表示往el节点上应用draggedClass样式
}
});
}
}
@HostListener('dragover',['$event'])//drag的对象在我的上面
onDragOver(ev:Event) {
ev.preventDefault();
ev.stopPropagation();
if(this.el.nativeElement === ev.target) {
this.data$.subscribe(dragData => {
if(this.dropTags.indexOf(dragData.tag) > -1) {
//设置data transfer的特效
this.rd.setProperty(ev, 'dataTransfer.effectAllowed','all');
this.rd.setProperty(ev, 'dataTransfer.dropEffect','move');
}else{
this.rd.setProperty(ev, 'dataTransfer.effectAllowed','none');
this.rd.setProperty(ev, 'dataTransfer.dropEffect','none');
}
})
}
}
@HostListener('dragleave',['$event'])//drag的对象离开我的领域
onDragLeave(ev:Event) {
ev.preventDefault();
ev.stopPropagation();
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)
}
});
}
}
@HostListener('drop',['$event'])//监听放的事件
onDrop(ev:Event) {
ev.preventDefault();
ev.stopPropagation();
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);
this.dropped.emit(dragData);// 把dropData发射出去
this.service.clearDragData();//在放下的时候执行service的clear操作,否则会影响下一次的拖拽
}
});
}
}
}
right-div.component.css文件定义拖拽悬浮的样式
.drag-enter {
background-color: dimgray;
}
right-div.component.html文件定义拖拽悬浮的html
<div class="rightDivBackground"
app-droppable
[dropTags]="['div-item','div-list']"
[dragEnterClass]="'drag-enter'"
[app-draggable]="true"
[dragTag]="'div-list'"
[draggedClass]="'drag-start'"
[dragData]="items"
(dropped)="handleMove($event)"
>
<div *ngFor="let item of items; let i = index" class="item">{{item.value}}</div>
</div>
<!-- 此元素既有拖也有放 -->
创建一个drag-drop service ng g s directive/drag-drop
,并添加到privides中
drag-drop.service.ts定义拖放时的服务service,用于存放拖放元素的信息
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
export interface DragData {
tag: string;//标记是哪个拖拽(在多级拖拽中),用户自定义
data: any;//表示传递的内容
}
@Injectable()
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);
}
}
在app.component中存放元素的信息并进行数据的变更,对item的内容进行处理
<div class="row">
<div class="col-6">
<app-left-div
[items]="list1">
</app-left-div>
</div>
<div class="col-6">
<app-right-div
[items]="list2"
(changeItem)="handleResult($event)">
</app-right-div>
</div>
</div>
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'Action';
list1 = [
{key:1,value:'item1'},
{key:2,value:'item2'},
{key:3,value:'item3'}
];
list2 = [
{key:4,value:'item4'},
{key:5,value:'item5'},
{key:6,value:'item6'}
];
handleResult(item) {
this.list1 = this.list1.filter((listItem)=>{
if(item.key === listItem.key){
return false;
}else{
return true;
}
});
this.list2.push(item);
}
}