1
1
import type { Observable } from 'rxjs' ;
2
- import { catchError , map , of } from 'rxjs' ;
2
+ import { catchError , finalize , map , of , switchMap , tap , timer } from 'rxjs' ;
3
3
import { inject , Injectable , signal } from '@angular/core' ;
4
4
import type { AbstractControl , AsyncValidator , ValidationErrors } from '@angular/forms' ;
5
5
import { PokemonService } from '~features/pokemon/services/pokemon.service' ;
@@ -8,30 +8,40 @@ import { PokemonService } from '~features/pokemon/services/pokemon.service';
8
8
export class PokemonValidator implements AsyncValidator {
9
9
private readonly pokemonService = inject ( PokemonService ) ;
10
10
private readonly pokemonName = signal ( '' ) ;
11
+ private readonly debounceMs = 500 ;
11
12
12
13
readonly pokemonId = signal ( - 1 ) ;
13
14
readonly isPokemonValidating = signal ( false ) ;
14
15
15
- validate ( control : AbstractControl ) : Observable < ValidationErrors | null > {
16
+ validate ( control : AbstractControl < string | null > ) : Observable < ValidationErrors | null > {
16
17
const pokemonName = ( control . value ?? '' ) . toLowerCase ( ) . trim ( ) ;
17
18
18
19
if ( ! pokemonName ) {
19
20
this . isPokemonValidating . set ( false ) ;
21
+ this . pokemonId . set ( - 1 ) ;
20
22
return of ( null ) ;
21
23
}
22
24
23
- this . pokemonName . set ( pokemonName . toLowerCase ( ) ) ;
25
+ this . pokemonName . set ( pokemonName ) ;
24
26
this . isPokemonValidating . set ( true ) ;
25
- return this . pokemonService . getPokemon ( pokemonName . toLowerCase ( ) ) . pipe (
26
- map ( ( pokemon ) => {
27
+ return this . validatePokemonName ( pokemonName ) . pipe (
28
+ finalize ( ( ) => {
27
29
this . isPokemonValidating . set ( false ) ;
28
- this . pokemonId . set ( pokemon . id ) ;
29
- return null ;
30
- } ) ,
31
- catchError ( ( ) => {
32
- this . isPokemonValidating . set ( false ) ;
33
- return of ( { pokemonName : true } ) ;
34
30
} ) ,
35
31
) ;
36
32
}
33
+
34
+ private validatePokemonName ( pokemonName : string ) : Observable < ValidationErrors | null > {
35
+ return timer ( this . debounceMs ) . pipe (
36
+ switchMap ( ( ) =>
37
+ this . pokemonService . getPokemon ( pokemonName ) . pipe (
38
+ tap ( ( pokemon ) => {
39
+ this . pokemonId . set ( pokemon . id ) ;
40
+ } ) ,
41
+ map ( ( ) => null ) ,
42
+ catchError ( ( ) => of ( { pokemonName : true } ) ) ,
43
+ ) ,
44
+ ) ,
45
+ ) ;
46
+ }
37
47
}
0 commit comments