TypeScript Question

Can't access to nested forms values to validate form

I'm having a problem validating nested forms using Reactive Forms in Angular. I had a sign up form that was working properly but after I added form groups to check the validity separately I get some errors. Those errors are because the validators works just on the main form and not the nested ones. I tried to access the value of the nested forms but I'm getting that the value of the form is undefined.

component.ts

import { Component, OnInit } from "@angular/core";
import { FormBuilder, FormGroup, Validators, FormControl } from "@angular/forms";

@Component({
selector: 'my-page-sign-up',
styleUrls: ['./sign-up.component.scss'],
templateUrl: './sign-up.component.html'
})

export class PageSignUpComponent implements OnInit {

myForm: FormGroup;

constructor(private fb: FormBuilder) {
this.myForm = this.fb.group({
'emailPass': this.fb.group({
email: ['', [
Validators.required,
Validators.email
]],
password: ['', Validators.required],
confirmPassword: ['', Validators.compose([
Validators.required,
this.isEqualPassword.bind(this)
])],
}),
})
}

isEqualPassword(control: FormControl): {[s: string]: boolean} {
if (!this.myForm.controls) {
return {passwordsNotMatch: true};

}
if (control.value !== this.myForm.controls['password'].value) {
return {passwordsNotMatch: true};
}
}
}


html

<form [formGroup]="myForm" (ngSubmit)="onSignup()">
<div formGroupName="emailPass" class="form-group">
<md-input-container class="col-md-6 md-icon-left">
<md-icon class="material-icons">mail_outline</md-icon>
<input formControlName="email" mdInput #email type="email" name="email" class="form-control" placeholder="Email">
<!--md-hint *ngIf="!myForm.get(['emailPass', 'email']).pristine && myForm.hasError('noEmail', 'email')">Invalid mail address</md-hint-->
</md-input-container>
<md-input-container class="col-md-6 md-icon-left no-margin-bottom">
<md-icon class="material-icons">lock_outline</md-icon>
<input required mdInput formControlName="password" type="password" class="form-control" id="password" name="password" placeholder="Contraseña">
</md-input-container>
<md-input-container class="col-md-6 md-icon-left no-margin-bottom">
<md-icon class="material-icons">lock_outline</md-icon>
<input required mdInput type="password" formControlName="confirmPassword" class="form-control" name="password" placeholder="Confirmar Contraseña">
</md-input-container>
</div>
<button md-raised-button type="submit">Siguiente</button>




So, I have two questions. The first one is how do I fix the error I'm getting trying to validate password and email. The second one is to know how can I disabled the button if the nested form (emailPass form) is not valid.

Thanks for your help as always!

Answer Source

Please check out this answer I gave for an almost similar question comparing two sibling formControls.

So based on that,

import { Component, OnInit } from "@angular/core";
import { FormBuilder, FormGroup, Validators, FormControl } from "@angular/forms";

function isEqualPassword(control: AbstractControl): {[s: string]: boolean} {
 if (!control.get('password') || !control.get('confirmPassword')) {
   return null; // returning validity true if either one of the controls not present - safety check on initial load
 }
 return control.get('password').value === control.get('confirmPassword').value ? null : {passwordsNotMatch: true};
}

@Component({
  selector: 'my-page-sign-up',
  styleUrls: ['./sign-up.component.scss'],
  templateUrl: './sign-up.component.html'
})

export class PageSignUpComponent implements OnInit {

 myForm: FormGroup;

 constructor(private fb: FormBuilder) {
   this.myForm = this.fb.group({
     emailPass: this.fb.group({
       email: ['', [
         Validators.required,
         Validators.email
       ]],
       passwordsCtrl: this.fb.group({
         password: ['', Validators.required],
         confirmPassword: ['', Validators.required]
         }, { validator: isEqualPassword }
       })
     })
   })
 }     
}

Then wrap your password related controls in another div or something similar (your choice) in another formGroupName to denote the newly created wrapping formGroup for these 2 controls.

<form [formGroup]="myForm" (ngSubmit)="onSignup()">
<div formGroupName="emailPass" class="form-group">
    <md-input-container class="col-md-6 md-icon-left">
        <md-icon class="material-icons">mail_outline</md-icon>
        <input formControlName="email" mdInput #email type="email" name="email" class="form-control" placeholder="Email">
        <!--md-hint *ngIf="!myForm.get(['emailPass', 'email']).pristine && myForm.hasError('noEmail', 'email')">Invalid mail address</md-hint-->
    </md-input-container>
    <div formGroupName="passwordsCtrl">
     <md-input-container class="col-md-6 md-icon-left no-margin-bottom">
         <md-icon class="material-icons">lock_outline</md-icon>
         <input required mdInput formControlName="password" type="password" class="form-control" id="password" name="password" placeholder="Contraseña">
     </md-input-container>
     <md-input-container class="col-md-6 md-icon-left no-margin-bottom">
         <md-icon class="material-icons">lock_outline</md-icon>
         <input required mdInput type="password" formControlName="confirmPassword" class="form-control" name="password" placeholder="Confirmar Contraseña">
     </md-input-container>
    </div>
</div>
<button md-raised-button type="submit">Siguiente</button>

Regarding disabling the button, your myForm and emailPass formGroups are essentially the same. If thats all you have in your form, you could as well omit either one of the two. So you can check for the form's validity using the valid flag of either myForm or the specific emailPass formGroup like this,

<button md-raised-button type="submit" [disabled]="!myForm.valid">Siguiente</button>

or

<button md-raised-button type="submit" [disabled]="!myForm.get('emailPass').valid">Siguiente</button>

Hope it helps.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download