Reputation: 5835
I'm trying to achieve that my async validator only gets triggered after I untouched an input field.
Right now I trigger the async validator with every letter I write into the input field. Since the async validator triggers a Firebase Cloud Function which even triggers a read in a Firebase Firestore it costs too much for only just checking if a specific username exists.
So is there a way to trigger the async validator only after an user touches another input field or a button? Or even something completely different which could solve this problem?
Following is my code:
SignUpComponent TS
export class SignUpComponent implements OnInit {
signUpForm: FormGroup;
constructor(
private authService: AuthService,
private userService: UserService,
private formBuilder: FormBuilder,
private router: Router) {
}
ngOnInit() {
this.signUpForm = this.formBuilder.group({
username: [null, Validators.required, this.existingUsername.bind(this)],
...
});
}
signUp() {
...
}
// performance is bad - one doc read and one function call for each letter entered
existingUsername(control: FormControl): Promise<any> | Observable<any> {
return this.userService.checkUsername(control.value)
.pipe(
map(res => {
return res.usernameAvailable ? null : {existingUsername: true};
})
);
}
}
SignUpComponent HTML
<form [formGroup]="signUpForm" (ngSubmit)="signUp()">
<mat-form-field>
<input matInput placeholder="Username" formControlName="username">
<mat-error *ngIf="signUpForm.get('username').hasError('required')">
Please insert a username!
</mat-error>
<mat-error *ngIf="signUpForm.get('username').hasError('existingUsername')">
Username is already in use!
</mat-error>
<mat-hint *ngIf="signUpForm.status === 'PENDING'">
Checking...
</mat-hint>
</mat-form-field>
</form>
Upvotes: 1
Views: 5632
Reputation: 29325
use the updateOn
option in your form control, something like:
username: [null, {
validators: Validators.required,
asyncValidators: this.existingUsername.bind(this),
updateOn: 'blur'}],
this tells angular to only run validation on blur rather than value change.
However, I do find this solution to be sub optimal in this use case, as users expect things to validate as they type. I find a better method is taking advantage of the way angular automatically cancels prior requests and switches on each new value by using ‘timer’ to debounce your validation, in your validator function, you’d just wrap your observable in something like:
return timer(300).pipe(switchMap(() => /* actual validator */ ))
This will make sure the validation request only runs if the user has not changed the value for 300ms, as the previous validation will be canceled if the user changes the value.
Upvotes: 3
Reputation: 2708
I think you want to update the form on blur
const control = new FormControl('', { updateOn: 'blur' });
Upvotes: 2