2

I'm having a hard time making distinctUntilChanged work in this next scenario. I made an asynchronous validator, which uses a service to check if a user exists with the given username. This validator is bound, as a directive, to an input.

class ValidateUniqueUsernameDirective implements AsyncValidator {
  constructor(private userService: UserService) {}

  validate(
    control: AbstractControl
  ): Promise<ValidationErrors> | Observable<ValidationErrors> {
    return control.valueChanges.pipe(
      debounceTime(1000),
      tap(value => {
        debugger;
        console.log(value);
      }),
      distinctUntilChanged(),
      switchMap(value => {
        return this.userService.getUserByUsername(value).pipe(
          map(user => {
            const usernameIsAvailable = user === undefined;
            return usernameIsAvailable
              ? null
              : { usernameAvailability: { value: control.value } };
          })
        );
      })
    );
  }
}

Calling the service results in network requests, so I debounced the validation, and, to further lessen them, I tried adding distinctUntilChanged, so that, as user @Kld explains here, user changes within the debounce time back to the previous value wouldn't trigger a new request. However, that's not what's happening. I don't understand what's happening, as the tapped value seems to be the same.

I'm on Angular 6, using RxJS 6. Thank you for your help!

n00dl3
  • 21,213
  • 7
  • 66
  • 76
cosh
  • 470
  • 1
  • 4
  • 15
  • 3
    everytime a new value is sent for checking, your `validate` function is called, a new observable is generated and the previous one is unscubscribed. – n00dl3 Jul 17 '18 at 11:51
  • Possible duplicate of [How to add debounce time to an async validator in angular 2?](https://stackoverflow.com/questions/36919011/how-to-add-debounce-time-to-an-async-validator-in-angular-2) – n00dl3 Jul 17 '18 at 11:54
  • @n00dl3, makes sense. then [why use distinctUntilChanged](https://stackoverflow.com/questions/46889851/formcontrol-detectchanges-why-use-distinctuntilchanged) with FormControl? – cosh Jul 17 '18 at 12:01
  • Kld is totally right. This is a valid point that doesn't apply to form validation but to other actions (eg: auto-saving forms). @cosh – n00dl3 Jul 17 '18 at 12:03
  • how so? if every time the user enters a new character, a new observable is generated, then distinctUntilChanged will always compare to a new value. Right? – cosh Jul 17 '18 at 12:06
  • 1
    Yes, that's the point: it does not apply to form validation. **But** it applies to other things, like I said, you can use dynamic forms to create a form that has an auto-save behavior, and you don't want to make a duplicated request. @cosh – n00dl3 Jul 17 '18 at 12:06

1 Answers1

2

Keep it simple:

class ValidateUniqueUsernameDirective implements AsyncValidator {

   private lastValue=null;
   private lastResult=null;
  constructor(private userService: UserService) {}

  validate(
    control: AbstractControl
  ): Promise<ValidationErrors> | Observable<ValidationErrors> {
      if(lastValue!==control.value){
        //do your validation and save result
      }else{
         // return last result or clear error state
      }
  }
}

You can also add timeouts to perform request after user stops typing etc.

Antoniossss
  • 31,590
  • 6
  • 57
  • 99