typescript 角咔嗒声去抖

shyt4zoc  于 2023-04-22  发布在  TypeScript
关注(0)|答案(7)|浏览(80)

在我的模板中,我有一个字段和两个按钮:

<div class="btn-plus" (click)="add(1)"> - </div>
<div class="txt"> {{ myValue }} </div>
<div class="btn-minus" (click)="add(-1)"> + </div>

在我的component .ts文件中,我有:

add(num) {
    this.myValue +=num;
    this.update(); // async function which will send PUT request
}

this.update()函数将myValue放在一个大JSON对象的适当字段中,并将其发送到服务器。

问题:当用户在短时间内点击10次加/减按钮时,则会发送10次请求。但我想只发送一次请求-最后一次点击后0.5秒。如何操作

gxwragnw

gxwragnw1#

使用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(打开控制台检查日志)

zbdgwd5y

zbdgwd5y2#

这是我在互联网上找到的部分答案,但我愿意接受更好的解决方案(或改进到以下解决方案(指令)):
在互联网上,我发现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 -->

奖励

指令appDebounceClickCory Rylan编写(我在这里放置代码,以防链接将来停止工作):

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);
  }
}
bvhaajcl

bvhaajcl3#

我最终使用了上面发布的DebounceClickDirective的简化版本。由于debounceTime运算符不支持前导/尾随选项,我决定使用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 },
  );
}
mwyxok5s

mwyxok5s4#

一个辅助功能--

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)事件。
不确定取消订阅是否真的有必要-实现比研究内存泄漏更快:)

o7jaxewo

o7jaxewo5#

如果不想使用rxjs observable示例,可以使用setTimeout实现。这将是ngOnDestroy上内存泄漏清理的理想实现:

@Component({
  selector: "app-my",
  templateUrl: "./my.component.html",
  styleUrls: ["./my.component.sass"],
})
export class MyComponent implements OnDestroy {
  timeoutRef: ReturnType<typeof setTimeout>;

  clickCallback() {
    clearTimeout(this.timeoutRef);
    this.timeoutRef  = setTimeout(()=> {
      console.log('finally clicked!')
    }, 500);
  }

  ngOnDestroy(): void {
    clearTimeout(this.timeoutRef);
  }
}

编辑:根据@lord-midi在评论中的建议,将timeoutRef TS def更新为更安全的版本。

aij0ehis

aij0ehis6#

在点击事件第一次,所以你不需要等待最后一次点击。或如何忽略后续事件?
Solution by Ondrej Polesny on freecodecamp Website也感谢Cory Rylan对Debouncer的精彩解释

import { Directive, EventEmitter, HostListener, Input, Output } from '@angular/core';

@Directive({
  selector: '[appDebounceClick]'
})

export class DebounceClickDirective {

  @Input() debounceTime: number = 800;
  @Output() debounceClick: EventEmitter<any> = new EventEmitter();

  private onClickDebounce = this.debounce_leading(
    (e) => this.debounceClick.emit(e), this.debounceTime
  );

  @HostListener('click', ['$event'])
  clickEvent(event) {
    event.preventDefault();
    event.stopPropagation();
    this.onClickDebounce(event);
  }

  private debounce_leading(func: any, timeout: number) {
    let timer;
    return (...args) => {
      if (!timer) {
        func.apply(this, args);
      }
      clearTimeout(timer);
      timer = setTimeout(() => {
        timer = undefined;
      }, timeout);
    };
  };

}
cfh9epnr

cfh9epnr7#

一个更容易理解的选择是使用一个自定义的主题,在点击时发出,并简单地使用rxjs原生debounceTime。Live Example(打开控制台日志):Stackblitz

// declare these variables
clicker = new Subject();
clicker$ = this.clicker.asObservable();

//Add to ngOnInit(), change the number according to how sensitive you want to debounce
this.clicker$.pipe(debounceTime(200)).subscribe(() => {
     console.log('Requesting Data ...');
     this.service.getData().subscribe((d) => console.log(d));
});

// your button's (click) function:
this.clicker.next(true);

相关问题