Angular 点击去抖动
Angular click debounce
在我的模板中有一个字段和两个按钮:
<div class="btn-plus" (click)="add(1)"> - </div>
<div class="txt"> {{ myValue }} </div>
<div class="btn-minus" (click)="add(-1)"> + </div>
在我的组件 .ts 文件中我有:
add(num) {
this.myValue +=num;
this.update(); // async function which will send PUT request
}
this.update()
函数将 myValue
放入大 JSON 对象的适当字段中,并将其发送到服务器。
问题:当用户在短时间内点击按钮plus/minus 10 次时,将发送10 次请求。但我只想发送一次请求 - 最后一次点击后 0.5 秒。 怎么做?
这是我在互联网上找到的部分答案,但我愿意接受更好的解决方案(或改进为以下解决方案(指令)):
在互联网上我找到了 appDebounceClick
指令,它以下列方式帮助我:
我从 .ts 文件中的 add
中删除了 update
:
add(num) {
this.myValue +=num;
}
并按以下方式更改模板:
<div
appDebounceClick
(debounceClick)="update()"
(click)="add(1)"
class="btn-plus"
> -
</div>
<div class="txt"> {{ myValue }} </div>
<!-- similar for btn-minus -->
奖金
指令 appDebounceClick
由 Cory Rylan 编写(我将代码放在这里以防 link 将来停止工作):
import { Directive, EventEmitter, HostListener, Input, OnDestroy, OnInit, Output } from '@angular/core';
import { Subject } from 'rxjs/Subject';
import { Subscription } from 'rxjs/Subscription';
import { debounceTime } from 'rxjs/operators';
@Directive({
selector: '[appDebounceClick]'
})
export class DebounceClickDirective implements OnInit, OnDestroy {
@Input() debounceTime = 500;
@Output() debounceClick = new EventEmitter();
private clicks = new Subject();
private subscription: Subscription;
constructor() { }
ngOnInit() {
this.subscription = this.clicks.pipe(
debounceTime(this.debounceTime)
).subscribe(e => this.debounceClick.emit(e));
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
@HostListener('click', ['$event'])
clickEvent(event) {
event.preventDefault();
event.stopPropagation();
this.clicks.next(event);
}
}
使用 takeUntil
运算符:
export class AppComponent {
name = 'Angular';
calls = new Subject();
service = {
getData: () => of({ id: 1 }).pipe(delay(500)),
};
click() {
this.calls.next(true);
this.service.getData().pipe(
takeUntil(this.calls),
).subscribe(res => console.log(res));
}
}
Stackblitz(打开控制台检查日志)
辅助函数 --
export const debounced = (cb, time) => {
const db = new Subject();
const sub = db.pipe(debounceTime(time)).subscribe(cb);
const func = v => db.next(v);
func.unsubscribe = () => sub.unsubscribe();
return func;
};
那么一个例子可以是:
import { Component, OnInit } from '@angular/core';
import { debounced } from 'src/helpers';
@Component({
selector: 'app-example',
// Click calls `debouncedClick` instead of `myClick` directly
template: '<button (click)="debouncedClick($event)">Click This</button>'
})
export class Example implements OnDestroy {
debouncedClick; // Subject.next function
constructor() {
// Done in constructor or ngOnInit for `this` to resolve
this.debouncedClick = debounced($event => this.myClick($event), 800);
}
// Called after debounced resolves (800ms from last call)
myClick($event) {
console.log($event);
}
ngOnDestroy() {
// Stay clean!
this.debouncedFunc.unsubscribe();
}
}
也可以反转用法,在单击时调用 'myClick' 并让 debounced
回调执行所需的操作。经销商的选择。
我个人也是 (keyup)
事件。
不确定取消订阅是否真的有必要 - 实施起来比研究内存泄漏要快 :)
如果不想使用 rxjs observable
实例,您可以使用 setTimeout
来实现。这将是在 ngOnDestroy
:
上进行内存泄漏清理的理想实现
@Component({
selector: "app-my",
templateUrl: "./my.component.html",
styleUrls: ["./my.component.sass"],
})
export class MyComponent implements OnDestroy {
timeoutRef: NodeJS.Timeout;
clickCallback() {
clearTimeout(this.timeoutRef);
this.timeoutRef = setTimeout(()=> {
console.log('finally clicked!')
}, 500);
}
ngOnDestroy(): void {
clearTimeout(this.timeoutRef);
}
}
我最终使用了上面发布的 DebounceClickDirective
的简化版本。由于 debounceTime
运算符不支持 leading/trailing 选项,我决定使用 lodash
。 这消除了从点击到操作的延迟,在我的例子中是打开一个对话框并且非常烦人。
那我就这样用了<button (debounceClick)="openDialog()">
import { Directive, EventEmitter, HostListener, Output } from '@angular/core';
import { debounce } from 'lodash';
@Directive({
selector: 'button',
})
export class DebounceClickDirective {
@Output() debounceClick = new EventEmitter();
@HostListener('click', ['$event'])
debouncedClick = debounce(
(event: Event) => {
this.debounceClick.emit(event);
},
500,
{ leading: true, trailing: false },
);
}
在我的模板中有一个字段和两个按钮:
<div class="btn-plus" (click)="add(1)"> - </div>
<div class="txt"> {{ myValue }} </div>
<div class="btn-minus" (click)="add(-1)"> + </div>
在我的组件 .ts 文件中我有:
add(num) {
this.myValue +=num;
this.update(); // async function which will send PUT request
}
this.update()
函数将 myValue
放入大 JSON 对象的适当字段中,并将其发送到服务器。
问题:当用户在短时间内点击按钮plus/minus 10 次时,将发送10 次请求。但我只想发送一次请求 - 最后一次点击后 0.5 秒。 怎么做?
这是我在互联网上找到的部分答案,但我愿意接受更好的解决方案(或改进为以下解决方案(指令)):
在互联网上我找到了 appDebounceClick
指令,它以下列方式帮助我:
我从 .ts 文件中的 add
中删除了 update
:
add(num) {
this.myValue +=num;
}
并按以下方式更改模板:
<div
appDebounceClick
(debounceClick)="update()"
(click)="add(1)"
class="btn-plus"
> -
</div>
<div class="txt"> {{ myValue }} </div>
<!-- similar for btn-minus -->
奖金
指令 appDebounceClick
由 Cory Rylan 编写(我将代码放在这里以防 link 将来停止工作):
import { Directive, EventEmitter, HostListener, Input, OnDestroy, OnInit, Output } from '@angular/core';
import { Subject } from 'rxjs/Subject';
import { Subscription } from 'rxjs/Subscription';
import { debounceTime } from 'rxjs/operators';
@Directive({
selector: '[appDebounceClick]'
})
export class DebounceClickDirective implements OnInit, OnDestroy {
@Input() debounceTime = 500;
@Output() debounceClick = new EventEmitter();
private clicks = new Subject();
private subscription: Subscription;
constructor() { }
ngOnInit() {
this.subscription = this.clicks.pipe(
debounceTime(this.debounceTime)
).subscribe(e => this.debounceClick.emit(e));
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
@HostListener('click', ['$event'])
clickEvent(event) {
event.preventDefault();
event.stopPropagation();
this.clicks.next(event);
}
}
使用 takeUntil
运算符:
export class AppComponent {
name = 'Angular';
calls = new Subject();
service = {
getData: () => of({ id: 1 }).pipe(delay(500)),
};
click() {
this.calls.next(true);
this.service.getData().pipe(
takeUntil(this.calls),
).subscribe(res => console.log(res));
}
}
Stackblitz(打开控制台检查日志)
辅助函数 --
export const debounced = (cb, time) => {
const db = new Subject();
const sub = db.pipe(debounceTime(time)).subscribe(cb);
const func = v => db.next(v);
func.unsubscribe = () => sub.unsubscribe();
return func;
};
那么一个例子可以是:
import { Component, OnInit } from '@angular/core';
import { debounced } from 'src/helpers';
@Component({
selector: 'app-example',
// Click calls `debouncedClick` instead of `myClick` directly
template: '<button (click)="debouncedClick($event)">Click This</button>'
})
export class Example implements OnDestroy {
debouncedClick; // Subject.next function
constructor() {
// Done in constructor or ngOnInit for `this` to resolve
this.debouncedClick = debounced($event => this.myClick($event), 800);
}
// Called after debounced resolves (800ms from last call)
myClick($event) {
console.log($event);
}
ngOnDestroy() {
// Stay clean!
this.debouncedFunc.unsubscribe();
}
}
也可以反转用法,在单击时调用 'myClick' 并让 debounced
回调执行所需的操作。经销商的选择。
我个人也是 (keyup)
事件。
不确定取消订阅是否真的有必要 - 实施起来比研究内存泄漏要快 :)
如果不想使用 rxjs observable
实例,您可以使用 setTimeout
来实现。这将是在 ngOnDestroy
:
@Component({
selector: "app-my",
templateUrl: "./my.component.html",
styleUrls: ["./my.component.sass"],
})
export class MyComponent implements OnDestroy {
timeoutRef: NodeJS.Timeout;
clickCallback() {
clearTimeout(this.timeoutRef);
this.timeoutRef = setTimeout(()=> {
console.log('finally clicked!')
}, 500);
}
ngOnDestroy(): void {
clearTimeout(this.timeoutRef);
}
}
我最终使用了上面发布的 DebounceClickDirective
的简化版本。由于 debounceTime
运算符不支持 leading/trailing 选项,我决定使用 lodash
。 这消除了从点击到操作的延迟,在我的例子中是打开一个对话框并且非常烦人。
那我就这样用了<button (debounceClick)="openDialog()">
import { Directive, EventEmitter, HostListener, Output } from '@angular/core';
import { debounce } from 'lodash';
@Directive({
selector: 'button',
})
export class DebounceClickDirective {
@Output() debounceClick = new EventEmitter();
@HostListener('click', ['$event'])
debouncedClick = debounce(
(event: Event) => {
this.debounceClick.emit(event);
},
500,
{ leading: true, trailing: false },
);
}