2019独角兽企业重金招聘Python工程师标准>>>
在我的项目中,用户点击按钮后,如果网页响应慢一点,用户常会再次点击一下。结果就触发了两次 click 操作。 如果是查询还好,但如果是post,put请求时,可能就是大问题了。
方案一:
由于我用的是ng-zorro, 方案一是在组件中增加一个 isLoading=false 的变量, 按钮上指定它的 nzLoading="isLoading" 。 zorro 文档截图:
在click事件中:
doSomeClick(){
this.isLoading=true;
this.service.createxxxx().subscribe( ()=> this.isLoading=false );
}
问题:
1、页面上如果有多个button话,且都绑定到一个isLoading变量, 则在点击一个按钮时,所有按钮都禁用了。如果想每个按钮单独控制,那就需要为每个按钮分配一个变量,这样会引入非常多的变量,也是麻烦事。
方案二:
利用throttleTime 来防止用户两次点击,且希望用法改动非常小,比如
原来代码: (click)="login()"
新代码 : (click.once)="login()"
所以我们实现一个 click.once的指令即可:
import { Directive, Input, OnDestroy, OnInit, HostListener, Output, EventEmitter, Renderer2, ElementRef } from '@angular/core';
import { throttleTime } from 'rxjs/operators';
import { Subject, Subscription } from 'rxjs';
@Directive({
// tslint:disable-next-line:directive-selector
selector: '[click.once]'
})
export class OnceClickDirectiveDirective implements OnInit, OnDestroy {
// tslint:disable-next-line:no-output-rename
@Output('click.once') clickCall: EventEmitter = new EventEmitter();
@Input() duration = 2000; // 必须是数字,传入时要用绑定语法
private $sub = new Subject();
private subscription: Subscription;
constructor(
private renderer: Renderer2, // Angular 2.x导入Renderer
private element: ElementRef
) { }
ngOnInit() {
// 如此绑定事件亦可
// this.renderer.listen(
// this.element.nativeElement, 'click', event => {
// event.preventDefault();
// event.stopPropagation();
// this.$sub.next(event);
// }
// );
this.subscription = this.$sub.pipe(
throttleTime(this.duration)
).subscribe(e => {
this.clickCall.emit(e);
});
}
@HostListener('click', ['$event'])
clickEvent(event: MouseEvent) {
event.preventDefault(); // 通常是不需要冒泡的
event.stopPropagation();
this.$sub.next(event);
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
代码里的时间间隔设置2秒, 通常接口在这个时间内都能返回结果了。
优化:
1、这个实现没有任何禁用状态的效果, 用户可以连续点击,不过只响应一次。
如果点击后想产生遮罩层,可以在根组件中添加一个变量控制这个层的显示,然后引入一个全局的service来注册一个Subject对象。当点击时,就向subject对象emit() 一下,然后定时再清除遮罩层。
我懒得麻烦。就不添加了!