1
votes

I am trying to create a custom form validator, but I keep getting this error Cannot read property 'value' of null. I have been really struggling with is issue for days now and here is my FormGroup:

this.step1Form = this.fb.group({
                username: new FormControl('', {
                        validators: [Validators.required,
                        Validators.minLength(4),
                        this.checkUsername.bind(this)],
                }),
                email: new FormControl('', {
                        validators: [Validators.required,
                        Validators.pattern("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$")],
                }),
                password: new FormControl('', {
                        validators: [Validators.required,
                        Validators.minLength(8)],
                }),
                repeat: new FormControl('', {
                        validators: [Validators.required,
                        Validators.minLength(8)],
                }),
                });

Here is my checkUsername method

checkUsername(g: FormGroup) {
                return this.dataService.checkUsername(g.get('username').value).pipe(map(data => {
                        return typeof data["error"] != 'undefined' ? null : {'taken': true};
                }));
        }

and here is the checkUsername method in this.dataService

checkUsername(username):Observable<any> {
            return this.http.get(globals.baseUrl+'/user/' + username, {headers:this.utilService.getHeadersJson()}).map(this.utilService.map);
        }

And I get the error:

Cannot read property 'value' of null

on this line

return this.dataService.checkUsername(g.get('username').value).pipe(map(data => {

What am I doing wrong? I have tried without the pipe like so

checkUsername(g: FormGroup) {
    return this.dataService.checkUsername(g.get('username').value).subscribe(data => {
            return typeof data["error"] != 'undefined' ? null : {'taken': true};
    });
}

but that didnt work, I have also tried my validator in different positions like so:

this.step1Form = this.fb.group({
        username: new FormControl('', {
                validators: [Validators.required,
                Validators.minLength(4)],
        }),
        email: new FormControl('', {
                validators: [Validators.required,
                Validators.pattern("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$")],
        }),
        password: new FormControl('', {
                validators: [Validators.required,
                Validators.minLength(8)],
        }),
        repeat: new FormControl('', {
                validators: [Validators.required,
                Validators.minLength(8)],
        }),
        }, { validator: this.checkUsername.bind(this)});

Also didnt work.

PLEASE HELP!

I have also tried this:

username: new FormControl('', {
                        validators: [Validators.required,
                        Validators.minLength(4)],
                        asyncValidator: [this.checkUsername.bind(this)],

 }),

got this error:

Argument of type '{ validators: ValidatorFn[]; asyncValidator: any[]; }' is not assignable to parameter of type 'ValidatorFn | AbstractControlOptions | ValidatorFn[]'. Object literal may only specify known properties, and 'asyncValidator' does not exist in type 'ValidatorFn | AbstractControlOptions | ValidatorFn[]'.

1
Since you are getting the error Cannot read property 'value' of null at this g.get('username'), you should first check if the FormGroup has a value for the key username.Deep
How would I do that @Deepuser979331
try to console log the value the variable g is holding inside the checkUsername function.Deep
have checked that g is actually the formGroup you want it to be. I dont think so. Thats probably the cause of your problem. Also i think this.checkUsername.bind(this)], this statement as well.Manish

1 Answers

1
votes

Angular reactive forms call the validators immediately and often. The cross-field validator on your form group will begin to validate as each child control is instantiated by the framework.

To prevent it from throwing premature validation errors, you'll probably want to ensure that the control in question has been instantiated, like so:

checkUsername(g: FormGroup) {
    if (!g || !g.get('username')) {
        return null;
    }
    return this.dataService.checkUsername(g.get('username').value).subscribe(data => {
        return typeof data["error"] != 'undefined' ? null : {'taken': true};
    });
}

Your 2nd attempt seems like a better direction to go, however, because it's an asynchronous validator for the 'username' control, but you have a typo in the asyncValidators property (missing the 's'). You also likely don't need to .bind(this) if you're not referencing "this" in the function.

username: new FormControl('', {
  validators: [Validators.required, Validators.minLength(4)],
  asyncValidators: [this.checkUsername]
})

Then your validator is just handling a single form control instead of the whole group, something like this:

checkUsername(c: FormControl) {
    if (!c) { return null; }
    return this.dataService.checkUsername(c.value).subscribe(data => {
        return typeof data["error"] != 'undefined' ? null : {'taken': true};
    });
}