Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular4: Form invalid despite no errors in fields (using custom validation)

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('[email protected]', 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.

like image 835
Carsten Ennulat Avatar asked Dec 06 '17 11:12

Carsten Ennulat


People also ask

What is dirty in Angular?

When the user changes the value in the watched field, the control is marked as "dirty" When the user blurs the form control element, the control is marked as "touched"

What is difference between touched and dirty in Angular?

The difference between touched and dirty is that with touched the user doesn't need to actually change the value of the input control. touched is true of the field has been touched by the user, otherwise it's false. The opposite of touched is the property untouched .


1 Answers

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());
like image 60
Tzannetos Philippakos Avatar answered Sep 18 '22 22:09

Tzannetos Philippakos