前言

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技術棧上。

需要源碼的同學移步:專欄簡介


推薦閱讀:
相关文章