Angular中自定义Debounce Click指令防止重复点击


Posted in Javascript onJuly 26, 2017

在这篇文章中,我们将介绍使用 Angular Directive API 来创建自定义 debounce click 指令。该指令将处理在指定时间内多次点击事件,这有助于防止重复的操作。

对于我们的示例,我们希望在产生点击事件时,实现去抖动处理。接下来我们将介绍 Directive API,HostListener API 和 RxJS 中 debounceTime 操作符的相关知识。首先,我们需要创建 DebounceClickDirective 指令并将其注册到我们的 app.module.ts 文件中:

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 指令是没有模板的组件,我们将使用以下方式应用上面的自定义指令:

<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() 方法。

在事件处理函数中,我们可以调用 event.preventDefault()event.stopPropagation() 方法来阻止浏览器的默认行为和事件冒泡。

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() 方法。

但我们不想立即发出点击事件,我们想做去抖动处理。为了实现这个功能,我们将使用 RxJS 中的 Subject 类。在我们的代码中,我们创建一个主题来处理我们的点击事件。在我们的方法中,我们调用 next() 方法来让 Subject 对象发出下一个值。此外我们也使用 RxJS 中 debounceTime 的操作符,这允许我们通过设置给定的毫秒数来去抖动点击事件。

一旦我们设置好了,我们现在可以在下面的模板中监听我们的自定义去抖动点击事件。

<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>

参考资源

creating-a-custom-debounce-click-directive-in-angular

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
Javascript客户端将指定区域导出到Word、Excel的代码
Oct 22 Javascript
jQuery实现动画效果的实例代码
May 07 Javascript
Javascript call和apply区别及使用方法
Nov 14 Javascript
Javascript 多物体运动的实现
Dec 24 Javascript
JavaScript中的立即执行函数表达式介绍
Mar 15 Javascript
jQuery插件slick实现响应式移动端幻灯片图片切换特效
Apr 12 Javascript
jQuery点击按钮弹出遮罩层且内容居中特效
Dec 14 Javascript
JavaScript编写点击查看大图的页面半透明遮罩层效果实例
May 09 Javascript
jQuery插件FusionCharts实现的3D柱状图效果实例【附demo源码下载】
Mar 03 Javascript
jQuery使用JSONP实现跨域获取数据的三种方法详解
May 04 jQuery
javascript 关于赋值、浅拷贝、深拷贝的个人理解
Nov 01 Javascript
html中创建并调用vue组件的几种方法汇总
Nov 17 Javascript
JavaScript利用fetch实现异步请求的方法实例
Jul 26 #Javascript
深入探究angular2 UI组件之primeNG用法
Jul 26 #Javascript
WdatePicker.js时间日期插件的使用方法
Jul 26 #Javascript
关于Stream和Buffer的相互转换详解
Jul 26 #Javascript
JS 60秒后重新发送验证码的实例讲解
Jul 26 #Javascript
JS数组操作中的经典算法实例讲解
Jul 26 #Javascript
你有必要知道的10个JavaScript难点
Jul 25 #Javascript
You might like
PHP OPP机制和模式简介(抽象类、接口和契约式编程)
2014/06/09 PHP
浅谈laravel数据库查询返回的数据形式
2019/10/21 PHP
php pdo连接数据库操作示例
2019/11/18 PHP
javascript showModalDialog,open取得父窗口的方法
2010/03/10 Javascript
JS将数字转换成三位逗号分隔的样式(示例代码)
2014/02/19 Javascript
JS实现控制表格行文本对齐的方法
2015/03/30 Javascript
浅谈jQuery animate easing的具体使用方法(推荐)
2016/06/17 Javascript
JavaScript实现左右下拉框动态增删示例
2017/03/09 Javascript
微信小程序 wx.request方法的异步封装实例详解
2017/05/18 Javascript
Vue2.0 组件传值通讯的示例代码
2017/08/01 Javascript
vscode下的vue文件格式化问题
2018/11/28 Javascript
微信小程序模板消息限制实现无限制主动推送的示例代码
2019/08/27 Javascript
Nodejs封装类似express框架的路由实例详解
2020/01/05 NodeJs
Vue中关闭弹窗组件时销毁并隐藏操作
2020/09/01 Javascript
[01:03:22]LGD vs OG 2018国际邀请赛淘汰赛BO3 第一场 8.25
2018/08/29 DOTA
[01:26]神话结束了,却也刚刚开始——DOTA2新英雄玛尔斯驾临战场
2019/03/10 DOTA
[01:35:53]完美世界DOTA2联赛PWL S3 Magma vs GXR 第二场 12.13
2020/12/17 DOTA
常用python数据类型转换函数总结
2014/03/11 Python
Python中使用装饰器和元编程实现结构体类实例
2015/01/28 Python
浅析Python中的序列化存储的方法
2015/04/28 Python
Python变量作用范围实例分析
2015/07/07 Python
详解python的webrtc库实现语音端点检测
2017/05/31 Python
windows下python安装pip图文教程
2018/05/25 Python
Python基于多线程操作数据库相关问题分析
2018/07/11 Python
python实现flappy bird游戏
2018/12/24 Python
详解pandas DataFrame的查询方法(loc,iloc,at,iat,ix的用法和区别)
2019/08/02 Python
如何安装2019Pycharm最新版本(详细教程)
2019/09/26 Python
python获取网络图片方法及整理过程详解
2019/12/20 Python
PyTorch中model.zero_grad()和optimizer.zero_grad()用法
2020/06/24 Python
Notino法国:购买香水和化妆品
2019/04/15 全球购物
对外汉语专业大学生职业生涯规划范文
2014/09/13 职场文书
儿园租房协议书范本
2014/12/02 职场文书
优秀学生干部事迹材料
2014/12/24 职场文书
失恋33天观后感
2015/06/11 职场文书
《活见鬼》教学反思
2016/02/24 职场文书
JavaScript实现简单计时器
2021/06/22 Javascript