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 操作符 debounceTime
、distinctUntilChanged
和 switchMap
.
完成的
- 去抖时间:
此过滤器运算符仅在特定时间段后才发出值,如果在此时间段内发出多个值,则仅返回最后一个值。例如,如果 debounceTime 设置为 500ms(在我们的例子中),则只有每 500ms 发出的最后一个值通过此过滤器运算符。如需更多信息,请访问 Rxjs official docs.
debounceTime 是比计算更改的字母数更好的解决方案,以防止 API 调用每个更改。另外,不要将 denounceTime 增加太多,因为它可能会破坏用户体验。
- distinctUntilChanged:
顾名思义,仅当接收到的最新值与先前值不同时,值才会通过此过滤器运算符。如需更多信息,请访问 Rxjs official docs.
- switchMap:
这是一个高阶映射运算符,在某种意义上,它 returns 是给定输入的可观察值。这在我们的案例中特别有用,因为对于每个输入元素值更改,我们都需要点击后端服务 API 来获取一些建议值。这个运算符最有趣的部分是,当它收到一个新值时,同时对先前值的异步服务调用正在进行时,它会取消先前的请求并发送一个新的请求以获得最新的值。
我强烈建议您仔细阅读此 article,因为使用 switchMap
opreator 时会发生很多事情,我无法在这里解释所有内容。了解此运算符背后的概念很重要。
记住不要手动订阅 switchMap 运算符中的 observable。此 RXJS 运算符自动订阅新的 observble 并取消订阅旧的 observable。
您可以在我的示例中查看此示例的源代码 Stackblitz app。
编辑:更新链接和参考
我有一个 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 操作符 debounceTime
、distinctUntilChanged
和 switchMap
.
- 去抖时间:
此过滤器运算符仅在特定时间段后才发出值,如果在此时间段内发出多个值,则仅返回最后一个值。例如,如果 debounceTime 设置为 500ms(在我们的例子中),则只有每 500ms 发出的最后一个值通过此过滤器运算符。如需更多信息,请访问 Rxjs official docs.
debounceTime 是比计算更改的字母数更好的解决方案,以防止 API 调用每个更改。另外,不要将 denounceTime 增加太多,因为它可能会破坏用户体验。
- distinctUntilChanged:
顾名思义,仅当接收到的最新值与先前值不同时,值才会通过此过滤器运算符。如需更多信息,请访问 Rxjs official docs.
- switchMap:
这是一个高阶映射运算符,在某种意义上,它 returns 是给定输入的可观察值。这在我们的案例中特别有用,因为对于每个输入元素值更改,我们都需要点击后端服务 API 来获取一些建议值。这个运算符最有趣的部分是,当它收到一个新值时,同时对先前值的异步服务调用正在进行时,它会取消先前的请求并发送一个新的请求以获得最新的值。
我强烈建议您仔细阅读此 article,因为使用 switchMap
opreator 时会发生很多事情,我无法在这里解释所有内容。了解此运算符背后的概念很重要。
记住不要手动订阅 switchMap 运算符中的 observable。此 RXJS 运算符自动订阅新的 observble 并取消订阅旧的 observable。
您可以在我的示例中查看此示例的源代码 Stackblitz app。
编辑:更新链接和参考