在這篇文章中,咱們將介紹使用 Angular Directive API 來建立自定義 debounce click 指令。該指令將處理在指定時間內屢次點擊事件,這有助於防止重複的操做。html
對於咱們的示例,咱們但願在產生點擊事件時,實現去抖動處理。接下來咱們將介紹 Directive API,HostListener API 和 RxJS 中 debounceTime 操做符的相關知識。首先,咱們須要建立 DebounceClickDirective 指令並將其註冊到咱們的 app.module.ts
文件中:typescript
import { Directive, OnInit } from '@angular/core'; @Directive({ selector: '[appDebounceClick]' }) export class DebounceClickDirective implements OnInit { constructor() { } ngOnInit() { } } @NgModule({ imports: [BrowserModule], declarations: [ AppComponent, DebounceClickDirective ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
Angular 指令是沒有模板的組件,咱們將使用如下方式應用上面的自定義指令:bootstrap
<button appDebounceClick>Debounced Click</button>
在上面 HTML 代碼中的宿主元素是按鈕,接下來咱們要作的第一件事就是監聽宿主元素的點擊事件,所以咱們能夠將如下代碼添加到咱們的自定義指令中。瀏覽器
import { Directive, HostListener, OnInit } from '@angular/core'; @Directive({ selector: '[appDebounceClick]' }) export class DebounceClickDirective implements OnInit { constructor() { } ngOnInit() { } @HostListener('click', ['$event']) clickEvent(event: MouseEvent) { event.preventDefault(); event.stopPropagation(); console.log('Click from Host Element!'); } }
在上面的例子中,咱們使用了 Angular @HostListener
裝飾器,該裝飾器容許你輕鬆地監聽宿主元素上的事件。在咱們的示例中,第一個參數是事件名。第二個參數 $event
,這用於告訴 Angular 將點擊事件傳遞給咱們的 clickEvent()
方法。app
在事件處理函數中,咱們能夠調用 event.preventDefault()
和 event.stopPropagation()
方法來阻止瀏覽器的默認行爲和事件冒泡。ide
如今咱們能夠攔截宿主元素的 click
事件,此時咱們還須要有一種方法實現事件的去抖動處理,而後將它從新發送回父節點。這時咱們須要藉助事件發射器和 RxJS 中的 debounce 操做符。函數
import { Directive, EventEmitter, HostListener, OnInit, Output } from '@angular/core'; import { Subject } from 'rxjs/Subject'; import 'rxjs/add/operator/debounceTime'; @Directive({ selector: '[appDebounceClick]' }) export class DebounceClickDirective implements OnInit { @Output() debounceClick = new EventEmitter(); private clicks = new Subject<any>(); constructor() { } ngOnInit() { this.clicks .debounceTime(500) .subscribe(e => this.debounceClick.emit(e)); } @HostListener('click', ['$event']) clickEvent(event: MouseEvent) { event.preventDefault(); event.stopPropagation(); this.clicks.next(event); } }
在上面的代碼中,咱們使用 Angular @Output
屬性裝飾器和 EventEmitter
類,它們容許咱們在指令上建立自定義事件。要發出事件,咱們須要調用 EventEmitter
實例上的 emit()
方法。this
但咱們不想當即發出點擊事件,咱們想作去抖動處理。爲了實現這個功能,咱們將使用 RxJS 中的 Subject 類。在咱們的代碼中,咱們建立一個主題來處理咱們的點擊事件。在咱們的方法中,咱們調用 next()
方法來讓 Subject 對象發出下一個值。此外咱們也使用 RxJS 中 debounceTime
的操做符,這容許咱們經過設置給定的毫秒數來去抖動點擊事件。code
一旦咱們設置好了,咱們如今能夠在下面的模板中監聽咱們的自定義去抖動點擊事件。htm
<button appDebounceClick (debounceClick)="log($event)"> Debounced Click </button>
如今,當咱們點擊咱們的按鈕時,它將延遲 500 毫秒。 500毫秒後,咱們的自定義輸出屬性將會發出點擊事件。如今咱們有了基本的功能,咱們須要作一些清理工做,並增長一些其它的功能。
對於 RxJS 中 Observables 和 Subject 對象,一旦咱們再也不使用它們,咱們必須取消訂閱事件。若是咱們沒有執行取消訂閱操做,有可能會出現內存泄漏。
import { Directive, EventEmitter, HostListener, OnInit, Output, OnDestroy } from '@angular/core'; import { Subject } from 'rxjs/Subject'; import { Subscription } from "rxjs/Subscription"; import 'rxjs/add/operator/debounceTime'; @Directive({ selector: '[appDebounceClick]' }) export class DebounceClickDirective implements OnInit, OnDestroy { @Output() debounceClick = new EventEmitter(); private clicks = new Subject<any>(); private subscription: Subscription; constructor() { } ngOnInit() { this.subscription = this.clicks .debounceTime(500) .subscribe(e => this.debounceClick.emit(e)); } ngOnDestroy() { this.subscription.unsubscribe(); } @HostListener('click', ['$event']) clickEvent(event: MouseEvent) { event.preventDefault(); event.stopPropagation(); this.clicks.next(event); } }
要取消訂閱,咱們須要保存訂閱時返回的訂閱對象。當 Angular 銷燬組件時,它將調用 OnDestroy
生命週期鉤子,所以咱們能夠在這個鉤子中,執行取消訂閱操做。
咱們指令的功能已基本齊全,它能夠正常處理事件。接下來,咱們將添加一些更多的邏輯,以便咱們能夠自定義去抖動時間。爲此,咱們將使用 @Input
裝飾器。
import { Directive, EventEmitter, HostListener, OnInit, Output, OnDestroy, Input } from '@angular/core'; import { Subject } from 'rxjs/Subject'; import { Subscription } from "rxjs/Subscription"; import 'rxjs/add/operator/debounceTime'; @Directive({ selector: '[appDebounceClick]' }) export class DebounceClickDirective implements OnInit, OnDestroy { @Input() debounceTime = 500; @Output() debounceClick = new EventEmitter(); private clicks = new Subject<any>(); private subscription: Subscription; constructor() { } ngOnInit() { this.subscription = this.clicks .debounceTime(this.debounceTime) .subscribe(e => this.debounceClick.emit(e)); } ngOnDestroy() { this.subscription.unsubscribe(); } @HostListener('click', ['$event']) clickEvent(event: MouseEvent) { event.preventDefault(); event.stopPropagation(); this.clicks.next(event); } }
@Input
裝飾器容許咱們將自定義延遲時間傳遞到咱們的組件或指令中。在上面的代碼中,咱們能夠經過組件的輸入屬性,來指定咱們但願去抖動的時間。默認狀況下,咱們將其設置爲 500 毫秒。
<button appDebounceClick (debounceClick)="log($event)" [debounceTime]="300"> Debounced Click </button>