0
votes

This is the custom validator I have atm :

import { FormGroup } from '@angular/forms';

// custom validator to check that two fields match
export function MustMatch(controlName: string, matchingControlName: string) {
  return (formGroup: FormGroup) => {
    const control = formGroup.controls[controlName];
    const matchingControl = formGroup.controls[matchingControlName];

    if (matchingControl.errors && !matchingControl.errors.mustMatch) {
      // return if another validator has already found an error on the matchingControl
      return;
    }

// set error on matchingControl if validation fails
    if (control.value !== matchingControl.value) {
      return { mustMatch: true };
    } else {
      return null;
    }
  }
}

I have also made a formGroup in the ts file that has the 2 values newPass and confirmPass This is the code :

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

// custom validator to check if password confirmation matches
import { MustMatch } from "../../customValidators/mustMatch";

@Component({
  selector: 'app-change-password',
  templateUrl: './change-password.component.html',
  styleUrls: ['./change-password.component.scss']
})
export class ChangePasswordComponent implements OnInit {

  //formGroup met 2 formControlls, 1 voor gebruiekrsnaam en 1 voor wachtwoord
  changePasswordForm = new FormGroup({
    currentPass: new FormControl('', Validators.compose([Validators.required])),
    newPass: new FormControl('', Validators.compose([Validators.required])),
    confirmPass: new FormControl('',Validators.compose([Validators.required,])),
  },  [MustMatch('newPass', 'confirmPass')]);

  get f() { return this.changePasswordForm.controls; }

  constructor() { }

  ngOnInit() {
  }

}

Now I'm trying to use *ngIf to check if the custom validator gives an error, but I'm also checking on required. I'm using a span aswell to make sure the error only validates if the input field returns touched or dirty. Here is the code :

<div class="fullwidthContainer">
  <div class="container">
    <div class="login-container">

      <div class="form-control">

        <form [formGroup]="changePasswordForm" class="myForm">
          <div class="form-group">
            <input type="password" placeholder="Current Password" formControlName="currentPass">
          </div>

          <div class="form-group">
            <input type="password" placeholder="New Password" formControlName="newPass">
          </div>

          <div class="form-group">
            <input type="password" placeholder="Confirm New Password" formControlName="confirmPass">
            <span class="help-block" *ngIf="changePasswordForm.get('confirmPass').errors &&
           (changePasswordForm.get('confirmPass').touched || changePasswordForm.get('confirmPass').dirty)">
            <div *ngIf="f.confirmPass.errors.required">Confirm Password is required</div>
            <div *ngIf="f.confirmPass.errors.MustMatch">Passwords must match</div>
            </span>
          </div>
        </form>

      </div>
    </div>
    <app-button [buttonText]="'Confirm'" [routerLink]="['../userPage']"
    ></app-button>
  </div>
</div>

The problem now is that only the required validator works, not the custom MustMatch validator.

2
The MustMatch validator is a validator of the form group. So the error it generates will be located on the from group. Not on the confirmPass control. - JB Nizet
so should I change it to : <div *ngIf="changePasswordForm.errors.MustMatch">Passwords must match</div> - Anwar
No. To <div *ngIf="changePasswordForm.hasError'(MustMatch')">Passwords must match</div>. Don't access errors directly: it can be null. - JB Nizet
It still does not work. Have done exactly what you wrote, also tried mustMatch instead of MustMatch, also no succes. - Anwar
Post a stackblitz reproducing the issue. - JB Nizet

2 Answers

0
votes

here is a code I used in my app, hope it helps:

Basically it has two form groups, with one of them nested. Last div in html prints the match validation errors.

.ts file:

// a property for printing validation messages
validation_messages = {
    'current_password': [
      { type: 'required', message: 'Current password is required.' }
    ],
    'new_password': [
      { type: 'required', message: 'New password is required.' }
    ],
    'confirm_new_password': [
      { type: 'required', message: 'Confirm new password is required.' }
    ],
    'matching_passwords': [
      { type: 'areEqual', message: 'Password mismatch.' }
    ]
  };

ngOnInit() {
    this.matching_passwords_group = new FormGroup({
      new_password: new FormControl('', Validators.required),
      confirm_new_password: new FormControl('', Validators.required)
    }, (formGroup: FormGroup) => {
      return PasswordValidator.areEqual(formGroup);
    });

    this.update_password_form = this.formBuilder.group({
      current_password: new FormControl('', Validators.required),
      matching_passwords: this.matching_passwords_group,
    });
  }

validator:

import { FormControl, FormGroup } from '@angular/forms';

export class PasswordValidator {

    static areEqual(formGroup: FormGroup) {
        let val;
        let valid = true;

        for (let key in formGroup.controls) {
            if (formGroup.controls.hasOwnProperty(key)) {
                let control: FormControl = <FormControl>formGroup.controls[key];

                if (val === undefined) {
                    val = control.value
                } else {
                    if (val !== control.value) {
                        valid = false;
                        break;
                    }
                }
            }
        }

        if (valid) {
            return null;
        }

        return {
            areEqual: true
        };
    }
}

html:

    <form [formGroup]="update_password_form" (ngSubmit)="updatePassword(update_password_form.value)" >

            <div class="form-group row">
                <label class="col-sm-2 col-form-label">Current Password</label>
                <div class="col-sm-4">
                    <input type="password" class="form-control" formControlName="current_password" />
                </div>
                <div class="validation-errors col-sm-6">
                    <ng-container *ngFor="let validation of validation_messages.current_password">
                        <div class="font-italic error-message"
                            *ngIf="update_password_form.get('current_password').hasError(validation.type) 
                            && (update_password_form.get('current_password').dirty 
                            || update_password_form.get('current_password').touched)">
                            {{ validation.message }}
                        </div>
                    </ng-container>
                </div>
            </div>

            <div formGroupName="matching_passwords">
                <div class="form-group row">
                    <label class="col-sm-2 col-form-label">New Password</label>
                    <div class="col-sm-4">
                        <input type="password" class="form-control" formControlName="new_password" />
                    </div>
                    <div class="validation-errors col-sm-6">
                        <ng-container *ngFor="let validation of validation_messages.new_password">
                            <div class="font-italic error-message"
                                *ngIf="update_password_form.get('matching_passwords').get('new_password').hasError(validation.type) 
                                && (update_password_form.get('matching_passwords').get('new_password').dirty 
                                || update_password_form.get('matching_passwords').get('new_password').touched)">
                                {{ validation.message }}
                            </div>
                        </ng-container>
                    </div>
                </div>

                <div class="form-group row">
                    <label class="col-sm-2 col-form-label">Confirm New Password</label>
                    <div class="col-sm-4">
                        <input type="password" class="form-control" formControlName="confirm_new_password" />
                    </div>
                    <div class="validation-errors col-sm-6">
                        <ng-container *ngFor="let validation of validation_messages.confirm_new_password">
                            <div class="font-italic error-message"
                                *ngIf="update_password_form.get('matching_passwords').get('confirm_new_password').hasError(validation.type) 
                                && (update_password_form.get('matching_passwords').get('confirm_new_password').dirty 
                                || update_password_form.get('matching_passwords').get('confirm_new_password').touched)">
                                {{ validation.message }}
                            </div>
                        </ng-container>
                    </div>
                </div>
            </div>

          // this part prints match validation messages
           <div class="validation-errors">
            <div class="error-message">
                <div *ngIf="update_password_form.get('matching_passwords').errors?.areEqual">Password mismatch.
                </div>
            </div>
          </div>

</form>
0
votes

your error is on the group, not the control, and you have a typo, and you should null check:

<div *ngIf="changePasswordForm.errors?.mustMatch">Passwords must match</div>