前言

Directive即Angular中常说的"指令",在Angular中通过如下代码:

@Directive({
selector: [appMyDirctive]
})
export class MyDirective {
...
}

将一个类标注为指令(只指定selector是指令的最简化的定义)。这样我们在html模板中使用的时候就可以通过这样的方式:

<div MyDirective></div>

给使用了我们的指令的DOM中的元素添加自定义行为,如上述代码中的div。

本篇文章通过一个图片拖拽的例子让大家对Directive的使用有一个具体的了解。实现效果为:拖动一个图片到指定的框中,能显示图片。

拖放事件

首先我们需要对HTML中的拖放特性有一个简单的了解。拖放我们可以将它看作是一个传输数据的动作,它有一个(data source)以及一个目标(data target),也就是说拖放将数据从source传到了target。

拖放有如下对象/事件:

  • DataTransfer对象:我们通过这个对象来传递数据,使用方式为Event.dataTransfer。
  • draggable属性:即标签元素设置draggable=true才有效果。
  • ondragstart 事件:当拖拽元素开始被拖拽的时候触发的事件,此事件作用在被拖曳元素上。
  • ondragenter 事件:当拖曳元素进入目标元素的时候触发的事件,此事件作用在目标元素上。
  • ondragover 事件:拖拽元素在目标元素上移动的时候触发的事件,此事件作用在目标元素上。
  • ondragleave 事件:拖拽元素在目标元素上移动的时候触发的事件,此事件作用在目标元素上。
  • ondrop 事件:被拖拽的元素在目标元素上同时滑鼠放开触发的事件,此事件作用在目标元素上。
  • ondragend 事件:当拖拽完成后触发的事件,此事件作用在被拖曳元素上。
  • event.preventDefault() 方法:阻止默认的事件方法等执行。在ondragover中一定要执行preventDefault(),否则ondrop事件不会被触发。另外,如果是从其他应用软体或是文件中拖东西进来,尤其是图片的时候,默认的动作是显示这个图片或是相关信息,并不是真的执行drop。此时需要用用document的ondragover事件把它直接干掉。
  • event.setDataTransfer.effectAllowed 属性:就是拖拽的效果。
  • evetn.setDataTransfer.setDragImage() 方法:指定图片或者文档元素做拖动时的视觉效果。

上述是在HTML5中的拖放事件说明,在Angular稍显区别:

在Web Storm(弃坑Vscode :))的任意html文件中,我们利用IDE的提示可以看到,Angular中与HTML5的拖放事件也是一一对应的。

Start

新建工程

ng new DragDemo

加入Angular Material[可选]

这里呢直接使用Angular Material这个UI,大家可加可不加,自从经历调博客园样式事件之后,能不CSS则不CSS。

ng add @angular/material

创建指令文件

执行

ng g d drag-drop

生成指令文件与指令测试文件。如果你导入了Angular Material的话,再创建一个管理Angular Material的模块文件。

此时目录如下:

app/
├── app.component.css
├── app.component.html
├── app.component.spec.ts
├── app.component.ts
├── app.module.ts
├── app-routing.module.ts
├── drag-drop.directive.spec.ts
└── drag-drop.directive.ts //指令文件

0 directories, 8 files

创建HTML

先画一个放置图片的框,

然后加上appDragDrop指令。最终的html代码如下:

<div id="container" [ngClass]="{bg-border:true, dragEnterBg: isDragging}"
appDragDrop (dropHandler)="onDropHandler($event)" (dragHandler)="onDragHandler($event)">
<img [src]="imageSrc"/>
</div>

其中[ngClass]可以将CSS样式与ts代码绑定起来,这里的dragEnterBg就是灰色背景,当isDragging为true的时候,背景就会变灰。

dropHandler是放下的处理事件,就将图片数据传输过来。dragHandler是实时刷新isDragging变数的值。

指令

指令代码如下:

import {Directive, Output, HostListener, EventEmitter, Input, OnInit, Host} from @angular/core;

@Directive({
selector: [appDragDrop],
})
export class DragDropDirective {
@Output() dropHandler: EventEmitter<any> = new EventEmitter();
@Output() dragHandler: EventEmitter<any> = new EventEmitter();

public isDragging: boolean;
public isInvalid: boolean;
public imageSrc: string;

@HostListener(dragover) onDragOver(e) {
return false;
}
@HostListener(dragenter) onDragEnter() {
this.isDragging = true;
this.dragHandler.emit(this.isDragging);
}

@HostListener(dragleave) onDragLeave() {
this.isDragging = false;
this.dragHandler.emit(this.isDragging);
}

@HostListener(drop, [$event]) onDrop(e) {
e.preventDefault();
this.isDragging = false;
this.dragHandler.emit(this.isDragging);
this.handleInputChange(e);
}

handleInputChange(e) {
console.log(e);
const file = e.dataTransfer ? e.dataTransfer.files[0] : null;
const pattern = /.*png/;
const reader = new FileReader();
this.isInvalid = false;

if (!file.type.match(pattern)) {
this.isInvalid = true;
console.log(invalid format);
return this.dropHandler.emit({
event: e,
isInvalid: this.isInvalid
});
}

reader.onload = this.handleReaderLoaded.bind(this);
reader.readAsDataURL(file);
}

handleReaderLoaded(e) {
const reader = e.target;
this.imageSrc = reader.result;
this.dropHandler.emit({ event: e, invalidFlag: this.isInvalid });
}
}

通过FileReader来读取文件。并通过@Output将数据传输到指令附著的标签上。@HostListener即是监听指令附著在的标签中的事件。

拖放操作放下的效果如图:

最后

喜欢这篇文章欢迎关注这个专栏,日常笔记都会同步到我的公众号Plus技术栈上。

需要源码的同学移步:专栏简介


推荐阅读:
相关文章