I am validating user inputs using reactive form with built-in validators and a custom validator. And i would like to provide front-end feedback/errors to users, but i cannot seems to correctly get the validation error object back. I am not getting any errors or warnings and console logs seems correct, so i don't really know what went wrong. Here're my code:
----Validators service name.valid.ts:
import { FormGroup, FormBuilder, Validators, AbstractControl } from '@angular/forms';
import { AngularFirestore } from 'angularfire2/firestore';
import { map, take } from 'rxjs/operators';
export class nameValidator {
static validname(afs: AngularFirestore) {
return (control: AbstractControl) => {
const username = control.value.toLowerCase();
console.log('username='+username)
return afs.collection('usrs', ref => ref.where('name', '==', username) )
.valueChanges().pipe(
take(1),
map(res =>
{
(res.length>=1) ? { 'validname': fail } : null;
}
)
)
}
}
}
----Component relevant excerpts:
import { Component } from '@angular/core';
import { IonicPage, NavController, NavParams } from 'ionic-angular';
import { AngularFirestoreCollection,AngularFirestoreDocument, AngularFirestore} from 'angularfire2/firestore';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { nameValidator } from '../../validators/name.valid';
@IonicPage()
@Component({
selector: 'page-register',
templateUrl: 'register.html',
})
export class RegisterPage {
formOne: FormGroup;
constructor(public navCtrl: NavController, public navParams: NavParams
, private afs: AngularFirestore
, private fmBuilder: FormBuilder)
{
this.formOne = this.fmBuilder.group({
name : ['TestnameA', Validators.compose([Validators.required ,Validators.pattern('[a-zA-Z]*')])
,nameValidator.validname(this.afs)],
password : ['testaa', Validators.compose([Validators.required ,Validators.minLength(5)])],
});
}//end constructor
----Template/html relevant excerpts:
<ion-content>
<form novalidate [formGroup]="formOne">
<input id="name" type="text" formControlName="name" required>
<div *ngIf="name.invalid && name.dirty" >
{{ name.value }} is already taken
</div>
<div *ngIf="name.valid">
{{ name.value }} is available
</div>
<div *ngIf="name.pending">
Checking availability of {{ name.value }}
</div>
</form>
</ion-content>
When i input name that is already taken in my firestore db, the name will wrongly still be flagged as valid in the front-end.
But from debugging brekapoints in my nameValidator service code, I know that res.length is 1. So somehow, the validator is able to detect
the validation error, but did not return the correct status.
I have also noted that my validator is asynchronous, thus it is placed as the 3rd argument formbuilderGroup and i have also tried adding delays into my checks, but make no difference.
Will appreciate any advice. thanks.
In Angular 5, the Async validator service should be of the following syntax for it to work :
import { AsyncValidatorFn, AbstractControl, ValidationErrors } from '@angular/forms';
import { AngularFirestore } from 'angularfire2/firestore';
import { Observable } from "rxjs/Observable";
import { map, take } from 'rxjs/operators';
export class nameValidator {
static validname(afs: AngularFirestore): AsyncValidatorFn {
return (control: AbstractControl): Observable<ValidationErrors> => {
const username = control.value.toLowerCase();
console.log('username='+username)
return afs.collection('usrs', ref => ref.where('name', '==', username) )
.valueChanges().pipe(
take(1),
map(res =>
{
return (res.length>=1) ? { 'validname': fail } : null;
}
)
)
}
}
}