4
votes

Ive looked at multiple questions and all the answers are for setting the validators on a form group. I know how to this using the form builder as shown below.

 this.userForm = this.fb.group({
  id: '',
  firstName: ['', [Validators.required]],
  lastName: ['', [Validators.required]],
  email: ['', [Validators.required]],
  phoneNumber: ['', [Validators.required]],
  jobTitle: ['', [Validators.required]],
  team: ['', [Validators.required]],
  passwords: this.fb.group({
    password: '',
    confirmPassword: ''
  },{
    validator: Validators.compose([PasswordValidators.MatchPassword, PasswordValidators.PasswordRequirements])
  }),
  userValues: this.fb.array([])
});

Now my component has two modes, add and edit. If the mode is add then the password controls should use the required validator. I check the mode and if its not in edit mode i want to update the validators on the passwords formgroup to include the required validator. I have tried this in the ngInit event as shown below.

 // if not edit mode then the password fields are required, so we overwrite the default validators.
if(!this.isEditMode)
{
  console.log("user form mode: " + this.isEditMode)
  const passwordForm = <FormGroup>this.userForm.controls['passwords'];

   // try to set the validators on the form group as a whole.
  passwordForm.setValidators([PasswordValidators.MatchPassword, PasswordValidators.PasswordRequirements, Validators.required]);
  // try to set the password on the individual control.
  passwordForm.controls['password'].setValidators([PasswordValidators.MatchPassword, PasswordValidators.PasswordRequirements, Validators.required]);

}

Now the first attempt where i try to update the validators in the form group as a while makes no difference. The second attempt where we update the individual form control causes my validator to throw exceptions. So is it possible for me to apply it to the form group without having to change the way my validator works. The validator can be seen below.

import { AbstractControl, ValidatorFn } from '@angular/forms';

export class PasswordValidators {

    static MatchPassword(AC: AbstractControl){
        let password = AC.get('password').value; // to get value in input tag
        let confirmPassword = AC.get('confirmPassword').value; // to get value in input tag

         if(!password || password == "")
         {
             return null;
         }

         if(password != confirmPassword) {
             AC.get('confirmPassword').setErrors( {MatchPassword: true} );
         } else {
             return null;
         }
    }

    // Ensures the password meets the minimum standard. Password should be at least 8 char, contain one numeric, one character, and one special character.
    static PasswordRequirements(AC: AbstractControl) {
        let password = AC.get('password').value;

        if(!password || password == "")
        {
            return null;
        }

        if(password != password.match("^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{8,}$")) {
            AC.get('password').setErrors({PasswordRequirements: true})
        } else {
            return null;
        }

    }
}

Any suggestions on how i can achieve this would be great. Cheers.

1
You're mixing validators that validate a whole group (MatchPassword, PasswordRequirements), and which thus need to be set on the group, with a validator that validates a single control (Validators.required, which checks that an input value isn't null or empty), and which thus must be set on a control. It's also unclear why you're not setting the right validators from the start: you already know the mode of your component when creating the form group, don't you? - JB Nizet
yer that is true i do know the mode but i didn't want to have two separate form definitions for the sake of reducing the amount of code. Although its now sounding easier to just use that option. Thank you for the explanation on the validators, i have only been working with angular for the past few weeks so i have still got plenty to learn. - Tony_89

1 Answers

3
votes

To solved this since i know what state my form is in i literally just have two different form definitions as JB Nizet mentioned in his comment.

It dosent feel like the cleanest solution but for this use case it works.

if(!this.isEditMode) {
  this.userForm = this.fb.group({
    id: '',
    firstName: ['', [Validators.required]],
    lastName: ['', [Validators.required]],
    email: ['', [Validators.required]],
    phoneNumber: ['', [Validators.required]],
    jobTitle: ['', [Validators.required]],
    team: ['', [Validators.required]],
    passwords: this.fb.group({
      password:  ['', Validators.required],
      confirmPassword:  ['', Validators.required]
    },{
      validator: Validators.compose([PasswordValidators.MatchPassword, PasswordValidators.PasswordRequirements])
    }),
    userValues: this.fb.array([]),
    roles: []
  });
} else { 
  this.userForm = this.fb.group({
    id: '',
    firstName: ['', [Validators.required]],
    lastName: ['', [Validators.required]],
    email: ['', [Validators.required]],
    phoneNumber: ['', [Validators.required]],
    jobTitle: ['', [Validators.required]],
    team: ['', [Validators.required]],
    passwords: this.fb.group({
      password: '',
      confirmPassword: ''
    },{
      validator: Validators.compose([PasswordValidators.MatchPassword, PasswordValidators.PasswordRequirements])
    }),
    userValues: this.fb.array([]),
    roles: []
  });
}