Reputation: 357
I have a reactive form with one field and when the user stops typing, I want to print to the console the value. For this purpose I use the debounceTime()
when the value in the field is changed.
I read several questions about the use of the debounceTime()
like How to use debounceTime in an angular component? and Angular and debounce but my code prints the latest value more than once then the change is detected and the time in the debounceTime function is elapsed.
The input filed of the form is this:
<input matInput formControlName="name" (keyup)="onInputChange()">
The code in function onInputChange is this:
this.userForm.get("name").valueChanges
.pipe(
debounceTime(500)
)
.subscribe(res => {
console.log(res)
});
It the input is test
the result in console is the following image:
What could be wrong?
Upvotes: 3
Views: 1102
Reputation: 73357
As mentioned in other answer, don't have the keyup
handler. Just listen to valueChanges
. Since you want to make a http-request, we can use switchMap
. Also remember to unsubscribe to valueChanges
. We also probably want to filter out if user only types empty spaces. So I suggest the following:
import { Subscription } from 'rxjs';
import { debounceTime, switchMap, filter } from 'rxjs/operators';
// ...
formSub = new Subscription();
ngOnInit() {
this.formSub = this.userForm.get('name').valueChanges.pipe(
debounceTime(500),
filter(value => !!value.trim()),
switchMap((value) => {
// replace with your request
return this.myService.postData(value)
})
// replace MyInterface with your model
).subscribe((data: MyInterface) => console.log(data))
}
ngOnDestroy() {
this.formSub.unsubscribe();
}
Then to the issue, when you programatically set the value. We can use emitEvent: false
, which does not cause valueChanges
to fire:
this.userForm.get('name').setValue('my name', { emitEvent: false })
Upvotes: 1
Reputation: 6283
You are calling a function and subscribing on each key press, you only need to subscribe once and not call the (keyup) function as this will catch any changes to the input.
It looks like the function that is being called is likely leading to the creation of four subscriptions, hence four entries in the console.
<input matInput formControlName="name" (keyup)="onInputChange()">
Should just be
<input matInput formControlName="name">
Move this logic into your ngOnInit()
so the subscription is created early into the components life cycle and only once.
You can then make a no reference copy of the name and compare it in the valuesChanged()
block to ensure it does not match the original name before posting.
userForm = this.formBuilder.group({
name: ['']
)};
public originalName = '';
public ngOnInit(): void
{
this.userService.getUserById(this.userId).subscribe(res =>
{
this.originalName = JSON.parse(JSON.stringify(res.name));
this.userForm.controls['name'].setValue(res.name);
this.userForm.get("name").valueChanges.pipe(debounceTime(500)).
subscribe(res =>
{
if (res.name !== this.originalName)
{
// logic to post to server
}
});
});
}
Would also recommend a read of how to handle subscriptions when the components is destroyed, as this will not handle its self, there are a lot of resources available.
Upvotes: 4