前言
Directive即Angular中常说的"指令",在Angular中通过如下代码:
@Directive
将一个类标注为指令(只指定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技术栈上。
需要源码的同学移步:专栏简介