admin管理员组文章数量:1356588
I have an update()
method, with which I search for certain entries in a table and update/filter the entries for each keystroke inside an input field.
My goal is to wait about 400ms after each keystroke before firing the request into the backend so that I avoid too many meaningless requests.
Currently I have implemented it with the setTimeout()
function, but I am sure that there is a more elegant way with RxJS.
update(searchInput: string) {
setTimeout(() => {
this.myService.search(searchInput)
.subscribe((res) => {
this.myArray.content = res;
});
}, 400);
}
Does somebody has any idea?
I have an update()
method, with which I search for certain entries in a table and update/filter the entries for each keystroke inside an input field.
My goal is to wait about 400ms after each keystroke before firing the request into the backend so that I avoid too many meaningless requests.
Currently I have implemented it with the setTimeout()
function, but I am sure that there is a more elegant way with RxJS.
update(searchInput: string) {
setTimeout(() => {
this.myService.search(searchInput)
.subscribe((res) => {
this.myArray.content = res;
});
}, 400);
}
Does somebody has any idea?
Share Improve this question asked Dec 18, 2019 at 12:53 Codehan25Codehan25 3,01412 gold badges59 silver badges109 bronze badges 2- you need to use debounce instead – demkovych Commented Dec 18, 2019 at 12:55
- 1 angular.io/guide/… – yurzui Commented Dec 18, 2019 at 13:03
6 Answers
Reset to default 3What you are looking for is debounceTime. It waits for x miliseconds before emitting anything. Combining it with other operators to not overload your API would be a good choice.
Your observable would look like
const search$ = fromEvent(search, 'input').pipe(
debounceTime(400), // Wait 400 MS before outputting
distinctUntilChanged(), // Only output unique values
map(event => event.target.value), // Extract the value of the search
switchMap((search) => service.doApi(search)) // SwitchMap to cancel a previous search if it wouldn't have pleted
)
search$.subscribe() // These might leak. Keep them in an array and clean them up when the ponent unloads
Where the search element would be a viewChild of your ponent.
import { Component, ElementRef, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { fromEvent, Subscription } from 'rxjs';
import { debounceTime, distinctUntilChanged, map, switchMap } from 'rxjs/operators';
@Component({
selector: 'app-ponent',
templateUrl: './app.ponent.html',
styleUrls: ['./app.ponent.scss']
})
export class AppComponent implements OnInit, OnDestroy {
@ViewChild('yourInput', {static: true}) search: ElementRef;
searchSubscription: Subscription;
ngOnInit(): void {
const search$ = fromEvent(this.search.nativeElement, 'input').pipe(
debounceTime(400), // Wait 400 MS before outputting
distinctUntilChanged(), // Only output unique values
map((event: KeyboardEvent) => (event.target as HTMLInputElement).value), // Extract the value of the search
switchMap((search) => service.doApi(search)) // SwitchMap to cancel a previous search if it wouldn't have pleted
) // Lives forever
this.searchSubscription = search$.subscribe()
}
ngOnDestroy(): void {
this.searchSubscription.unsubscribe()
}
}
I've leave some ments in the code:
const search = fromEvent(searchInput, 'input').pipe(
debounceTime(1000), // Time in milliseconds between key events
distinctUntilChanged(), // If previous query is different from current
map(event => event.target.value) // get value,
filter(query => query) // if character length greater then 0,
tap(query => console.log(`About to make an API call with query: ${query}`)),
switchMap(getCars) // Your api call
);
search.subscribe(successCallback);
Try to use debounce()
and distinctUntileChanged()
:
handleFilterEvent = new Subject<any>();
ngOnInit(){
this.handleFilterEvent
.debounceTime(500)
.distinctUntilChanged()
.subscribe(value => {
this.myService.search(searchInput)
.subscribe((res) => {
this.myArray.content = res;
});
});
}
onSearchChange(value){
if(value) {
this.handleFilterEvent.next(value);
}
}
HTML:
<input type="text" class="form-control"
(input)="onSearchChange($event.target.value)">
You can achieve that using fromEvent
and Viewchild
Just reference the ViewChild as follow:
@ViewChild('yourInput') inputName: any;
Then you can simply use:
fromEvent(this.inputName.nativeElement, 'keyup')
.pipe(
map((k: any) => k.target.value),
debounceTime(1000)
)
.subscribe(value => {
foo();
});
This is true RXJS way, I have changed myArray.content
to be Observable
, as this way you can use piping and mapping. This will prevent multiple requests, more specifically, it will unsubscribe from the previous search before starting new one.
searchedInput$ = new Subject<string>();
myArray: { content: Observable<any> } = {};
constructor(myService: any) {
this.myArray.content = this.searchedInput$.pipe(
distinctUntilChanged(),
debounceTime(400),
switchMap(input => myService.search(input)),
);
}
update(searchInput: string): void {
this.searchedInput$.next(searchInput);
}
You can do the following:
In HTML:
<input type="text" #myId="ngModel" [(ngModel)]="data" />
In your ponent:
import {
debounceTime,
distinctUntilChanged
} from "rxjs/operators";
@ViewChild('myId', {static: true}) myControl: NgModel;
ngOnInit() {
this.myControl
.valueChanges // For each changes
.pipe(
debounceTime(400),
distinctUntilChanged()
)
.subscribe(() => ...)
}
本文标签: javascriptMore elegant way to subscribe with rjxs instead of setTimeout functionStack Overflow
版权声明:本文标题:javascript - More elegant way to subscribe with rjxs instead of setTimeout function? - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1744067053a2585193.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论