3

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:

enter image description here

What could be wrong?

Elias
  • 347
  • 2
  • 5
  • 20

2 Answers2

4

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.

devDan
  • 5,969
  • 3
  • 21
  • 40
  • thanks for the answer but this leads to another issue. I want this code to be used in order to update the name in a database. So in the ngOnInit() I give values to the form field: this.userForm.controls['name'].setValue("TheName); So when the filed is initiated the code that updates the name is called even though the user has not changed the name. How can my code be used in a function? – Elias Aug 21 '19 at 16:17
  • you should be able to have both in the ngoninit ? or is something preventing that probably the setValue bit first – devDan Aug 21 '19 at 17:09
  • it would be better for me if I could put in in a function, because I need to run it only when the user will stop writing. In ngOnInit() it will run every time the component is initialized and will create additional hits to the database – Elias Aug 21 '19 at 17:15
  • I would just add a check like if res.length make post else do nothing inside the subscription – devDan Aug 21 '19 at 17:24
  • It could work , but compare the res.length with what? – Elias Aug 21 '19 at 17:44
  • I modified the answer to match what I am trying to achieve. In the ngOnInit the form is initialized and if a user exists in the database the form is also initialized with the user name. So, the res.name.length is true and the logic is executed. Instead, the logic must be executed only if the value is changed – Elias Aug 22 '19 at 05:17
  • Made an edit, should help with the issue you are facing – devDan Aug 22 '19 at 07:10
  • Thank a lot for your time!! – Elias Aug 22 '19 at 16:27
1

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 })
AT82
  • 71,416
  • 24
  • 140
  • 167