1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > Angular 自定义拖拽指令

Angular 自定义拖拽指令

时间:2018-10-28 01:25:09

相关推荐

Angular 自定义拖拽指令

指令

组件是一种带模版的指令。指令是超级。

结构型指令(改变布局)和属性型指令(改变外观和行为)。

Renderer2和ElementRef

Angular不提倡直接操作DOM

对于DOM的操作应该通过Renderer2进行。

ElementRef是指向DOM元素的引用

拖拽指令实例

1、新建directive module

$ ng g m directiveCREATE 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中导入DirectiveModule

import { NgModule } from "@angular/core";import { CommonModule } from "@angular/common";import { MaterialModule } from "../material/material.module";import { ConfirmDialogComponent } from "./confirm-dialog/confirm-ponent";import { DirectiveModule } from '../directive/directive.module';@NgModule({imports: [CommonModule, MaterialModule, DirectiveModule],exports: [CommonModule, MaterialModule, DirectiveModule],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, DropDirective],})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虚线半透明

.drag-start {opacity: 0.5;border: #ff525b dashed 2px;}

<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, HostListener, ElementRef, Renderer2, 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 }}}

View Code

在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, DropDirective],exports: [DragDirective, DropDirective],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, HostListener, Host, ElementRef, Renderer2, 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,'draggable',`${value}`);}get isDraggable(){return this._isDraggble;}@Input()draggedClass:string;

//多定义一个dragTag@Input()dragTag:string;

//给DragDropservice传递的数据@Input()dragData:anyconstructor(private el:ElementRef,private rd:Renderer2,

//注入DragDropServiceprivate service:DragDropService) {}@HostListener('dragstart', ['$event'])ondragstart(ev:Event){//判断drag元素是不是指令应用的元素发起的if(this.el.nativeElement===ev.target){this.rd.addClass(this.el.nativeElement, this.draggedClass);//往el上增加一个class

//进入时候给service传递上数据

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);}}}

10,更新drop

对于drop来讲,它的tags是一个数组,而不是字符串了。

因为拖放放的区域,可能会支持多个拖的区域。

所以放的时候原来的判断都有问题,首先需要判断这个拖拽是不是你能够接收的。

建立一个私有的data$,在constructor里订阅data。

drop指令还需要一个Output,因为需要什么时候drop。

import { Directive, HostListener, ElementRef, Renderer2, 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 rd:Renderer2,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,'dataTransfer.effectAllowed','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上增加一个classthis.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-important':item.priority===2,'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;}}

最终效果:

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。