Angular 中自定義 Debounce Click 指令

在這篇文章中,咱們將介紹使用 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

Debounce Events

如今咱們能夠攔截宿主元素的 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毫秒後,咱們的自定義輸出屬性將會發出點擊事件。如今咱們有了基本的功能,咱們須要作一些清理工做,並增長一些其它的功能。

Unsubscribe

對於 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 生命週期鉤子,所以咱們能夠在這個鉤子中,執行取消訂閱操做。

Custom Inputs

咱們指令的功能已基本齊全,它能夠正常處理事件。接下來,咱們將添加一些更多的邏輯,以便咱們能夠自定義去抖動時間。爲此,咱們將使用 @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>

參考資源

相關文章
相關標籤/搜索