7

I'm trying to validate a form using custom validation. For some reason I have to build a form that can change either E-Mail or set a new Password. For that reason I cannot use Validators.required as password fields become only required when they are touched.

My problem is that when input validation is resolved the form is still invalid.

I've made a plnkr to demonstrate my problem:
http://plnkr.co/edit/obF4gC5RHkXOOlCEsIuH?p=preview

ngOnInit(): void {
    this.emailField = new FormControl('mail@mail.com', Validators.email);
    this.pw1 = new FormControl('', [this.passwordAreEqualValidator, Validators.minLength(2)]);
    this.pw2 = new FormControl('', this.passwordAreEqualValidator);
    this.pwOld = new FormControl('', this.notEmptyIfNewIsTouchedValidator);

    this.form = this.formBuilder.group({
      pw1: this.pw1,
      pw2: this.pw2,
      emailField: this.emailField,
      pwOld: this.pwOld
    });
  }

notEmptyIfNewIsTouchedValidator(control: FormControl) : ValidationErrors | null {
    if (control.dirty) {
      if (control.value === '') {
        return {
          oldPasswordEmpty: true
        }
      }

      const parent = control.parent,
            pw1 = parent.get('pw1'),
            pw2 = parent.get('pw2');

      // this will trigger nothing in template, this part is only
      // to mark form invalid if password was entered. passwordNew1
      // cannot be false by default as this is okay when you only
      // want to change your e-mail adress.
      if (pw1.value.length === 0) {
        pw1.setErrors({noPassword: true});
      } else {
        if (pw1.hasError('noPassword')) {
          let pw1Errors = pw1.errors;
          delete pw1Errors.noPassword;  
          pw1.setErrors(pw1Errors);
        }
      }

      console.log('pw1 errors: ', pw1.errors);
      console.log('pw2 errors: ', pw2.errors);
      console.log('old errors: ', control.errors);
    }

    return null;
  }

  passwordAreEqualValidator(control: FormControl): ValidationErrors | null {
    if (control.dirty) {
      const
        parent = control.parent,
        pwOld = parent.get('pwOld'),
        pw1 = parent.get('pw1'),
        pw2 = parent.get('pw2');

      if (pwOld.value === '') {
        pwOld.setErrors({oldPasswordEmpty: true})
      }

      if (pw1.value !== pw2.value) {
        return {
          notEqual: true
        };
      } else {
        let pw1Errors = pw1.errors;

        if (pw1.hasError('notEqual')) {
          delete pw1Errors.notEqual;
          pw1.setErrors(pw1Errors);
        }

        if (pw1.hasError('noPassword')) {
          delete pw1Errors.noPassword;
          pw1.setErrors(pw1Errors);
        }

        pw2.setErrors(null);

        console.log('pw1 errors: ', pw1.errors);
        console.log('pw2 errors: ', pw2.errors);
        console.log('old errors: ', pwOld.errors);
      }
    }

    return null;
  }

Steps to reproduce:

  1. Enter a password (error old password is empty shows up)
  2. Repeat Password (error password are not equal shows up until both password matches)
  3. once your repeating password is matching add a value for old password (error old password is removed)
  4. all validation errors are removed, but still form is not valid.

Why is my form invalid?

P.S: Once I edit my pw1 again, my form becomes valid, although the result of the errors did not change.

Carsten Ennulat
  • 156
  • 1
  • 9

2 Answers2

1

You have the Validator set on both fields..... what happens is that pw1 does not revalidate when you enter pw2, so pw1 is still invalid until you go back and type it in again.

In your component, at the end of your ngOnInit, you can add the following....

    this.pw1.valueChanges().subscribe(() => this.pw2.updateValueAndValidity());
    this.pw2.valueChanges().subscribe(() => this.pw1.updateValueAndValidity());
0

This answer is only related to the problem of a form that is invalid but has no errors, and not an answer to the identified problem.

I am creating subforms; my base FormGroup has both FormGroups and FormArrays as components.

this.form = this.formBuilder.group({
  name: FormControl('', []),
  addresses: this.formBuilder.array([
    this.formBuilder.group({
      street: FormControl('', []),
      city: FormControl('', []),
      state: FormControl('', []),
      zip: FormControl('', []),
    }),
    this.formBuilder.group({
      street: FormControl('', []),
      city: FormControl('', []),
      state: FormControl('', []),
      zip: FormControl('', []),
    }),
  ]),
  phone: FormControl('',[]),
});

I was getting the errors in the subforms, specifically in a FormGroup in the FormArray, but the top level form was not showing any errors. I had to look in the FormGroup in the FormArray.

Schlameel
  • 411
  • 1
  • 4
  • 9