Angular 如何在字段更新后执行函数。但是不是在每次更改时都这样做,而是仅在更改 3 或 4 个字母后才这样做

Angular how to execute a function after a field has been updated. But instead of doing it on every change, only after 3 or 4 letters changed

我有一个 angular 表单,我想建议用户根据他正在写的内容在输入中输入什么。

我显然可以使用指令 onChange 并对服务进行 api 调用,该服务会在每次更改后提出建议。但我认为这不是一个好的实现,因为我会根据用户写的字母(以及可能的拼写错误)进行尽可能多的调用。 因此,我们的想法是制作此 onChange 函数,该函数仅在对该字段进行 X 更改后才进行 api 调用。 我怎样才能做到这一点?我如何检查该字段是否已更改到足以进行另一个 api 调用以获取新建议?

正如另一位用户评论的那样,如果您想在用户输入时延迟对 api 的调用,则最好利用 debounceTime 运算符。但是,如果您想每 n 次更改都执行一次,则可以使用 tap 运算符并计算可观察对象发出的次数。

<input type="text" [formControl]="foo">
private count = 0;
private n     = 3;
public foo    = new FormControl('');

ngOnInit() {
 this.foo.valueChanges.pipe(
   tap(() => this.count++)
 ).subscribe(() => if (this.count % this.n === 0) { console.log('call api'); }
}

首先,您必须监听表单输入字段的变化。如果您使用的是 Reactive Angular 表单,那么您可以使用 formControl 的 valueChanges 属性 来监听变化。否则你必须使用 @ViewChild 装饰器来获取 TS 文件中输入元素的引用,然后使用 RXJS fromEvent 函数监听变化。

我已经使用 RXJS 的 fromEvent 函数进行演示。请根据您的需要随意修改它。

export class AppComponent implements OnInit {
  @ViewChild('textbox', { static: true }) textbox: ElementRef<HTMLInputElement>;

  ngOnInit() {
    /**
     * If you are using Reactive form, then use
     * form.get('<form-control-name>').valueChanges
     */
    const inputChanges$ = fromEvent(this.textbox.nativeElement, 'keyup');
    inputChanges$
      .pipe(
        // remove the below map operator if you are using Reactive forms formControl
        map(event => (event.target as HTMLInputElement).value),
        debounceTime(500),
        distinctUntilChanged(),
        switchMap(val => {
          // perform the http call for your service API like below
          // return this.http.get(`service.com/get?search=${val}`)
          return of(`sevice api all for: ${val}`);
        })
      )
      .subscribe(console.log)
  }
}

在上面的例子中,所有繁重的工作都是由这 3 个 RXJS 操作符 debounceTimedistinctUntilChangedswitchMap.

完成的
  1. 去抖时间:

此过滤器运算符仅在特定时间段后才发出值,如果在此时间段内发出多个值,则仅返回最后一个值。例如,如果 debounceTime 设置为 500ms(在我们的例子中),则只有每 500ms 发出的最后一个值通过此过滤器运算符。如需更多信息,请访问 Rxjs official docs.

debounceTime 是比计算更改的字母数更好的解决方案,以防止 API 调用每个更改。另外,不要将 denounceTime 增加太多,因为它可能会破坏用户体验。

  1. distinctUntilChanged:

顾名思义,仅当接收到的最新值与先前值不同时,值才会通过此过滤器运算符。如需更多信息,请访问 Rxjs official docs.

  1. switchMap:

这是一个高阶映射运算符,在某种意义上,它 returns 是给定输入的可观察值。这在我们的案例中特别有用,因为对于每个输入元素值更改,我们都需要点击后端服务 API 来获取一些建议值。这个运算符最有趣的部分是,当它收到一个新值时,同时对先前值的异步服务调用正在进行时,它会取消先前的请求并发送一个新的请求以获得最新的值。

我强烈建议您仔细阅读此 article,因为使用 switchMap opreator 时会发生很多事情,我无法在这里解释所有内容。了解此运算符背后的概念很重要。

记住不要手动订阅 switchMap 运算符中的 observable。此 RXJS 运算符自动订阅新的 observble 并取消订阅旧的 observable。

您可以在我的示例中查看此示例的源代码 Stackblitz app

编辑:更新链接和参考