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 相关文章推荐
从零开始学习jQuery (四) jQuery中操作元素的属性与样式
Feb 23 Javascript
使用javascript提交form表单方法汇总
Jun 25 Javascript
jQuery实现表格行和列的动态添加与删除方法【测试可用】
Aug 01 Javascript
React-router 4 按需加载的实现方式及原理详解
May 25 Javascript
探索webpack模块及webpack3新特性
Sep 18 Javascript
浅谈在vue中用webpack打包之后运行文件的问题以及相关配置方法
Feb 21 Javascript
详解在vue-cli项目下简单使用mockjs模拟数据
Oct 19 Javascript
vue中el-upload上传图片到七牛的示例代码
Oct 19 Javascript
详解create-react-app 2.0版本如何启用装饰器语法
Oct 23 Javascript
详解jQuery获取特殊属性的值以及设置内容
Nov 14 jQuery
vue history 模式打包部署在域名的二级目录的配置指南
Jul 02 Javascript
vue 把二维或多维数组转一维数组
Apr 24 Vue.js
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中文字符串截取多种方法汇总
2016/10/06 PHP
jquery下异步提交表单 异步跨域提交表单
2010/11/17 Javascript
JSON序列化与解析原生JS方法且IE6和chrome测试通过
2013/09/05 Javascript
js delete 用法(删除对象属性及变量)
2014/08/24 Javascript
JQuery的Ajax中Post方法传递中文出现乱码的解决方法
2014/10/21 Javascript
JavaScript数据类型详解
2015/04/01 Javascript
javascript密码强度校验代码(两种方法)
2015/08/10 Javascript
使用plupload自定义参数实现多文件上传
2016/07/19 Javascript
JS中script标签defer和async属性的区别详解
2016/08/12 Javascript
基于WebUploader的文件上传js插件
2016/08/19 Javascript
Vue.js中用v-bind绑定class的注意事项
2016/12/13 Javascript
微信小程序 图片边框解决方法
2017/01/16 Javascript
jQuery实现的3D版图片轮播示例【滑动轮播】
2019/01/18 jQuery
layui表格 列自动适应大小失效的解决方法
2019/09/06 Javascript
详解JavaScript中的数据类型,以及检测数据类型的方法
2020/09/17 Javascript
[04:23]DOTA2上海特锦赛小组赛第一日 TOP10精彩集锦
2016/02/27 DOTA
[28:05]完美世界DOTA2联赛循环赛Inki vs DeMonsTer 第一场 10月30日
2020/10/31 DOTA
重命名批处理python脚本
2013/04/05 Python
跟老齐学Python之不要红头文件(1)
2014/09/28 Python
Python实现测试磁盘性能的方法
2015/03/12 Python
python实现基于两张图片生成圆角图标效果的方法
2015/03/26 Python
pygame游戏之旅 按钮上添加文字的方法
2018/11/21 Python
Python基础之函数的定义与使用示例
2019/03/23 Python
tensorboard实现同时显示训练曲线和测试曲线
2020/01/21 Python
keras实现基于孪生网络的图片相似度计算方式
2020/06/11 Python
哪些是python中web开发框架
2020/06/17 Python
Python操作Word批量生成合同的实现示例
2020/08/28 Python
Python实现加密的RAR文件解压的方法(密码已知)
2020/09/11 Python
论文指导教师评语
2014/04/28 职场文书
听课评语大全
2014/04/30 职场文书
关于爱国的标语
2014/06/24 职场文书
无刑事犯罪记录证明范本
2014/09/29 职场文书
故宫的导游词
2015/01/31 职场文书
2016情人节宣传语
2015/07/14 职场文书
网络研修心得体会
2016/01/08 职场文书
环境保护宣传标语大全!
2019/06/28 职场文书