Confirm password validation in Angular 6

You can simply use password field value as a pattern for confirm password field.

For Example:

<div class="form-group">
    <input type="password" [(ngModel)]="userdata.password" name="password" placeholder="Password" class="form-control"
        required #password="ngModel" pattern="(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,}" />
    <div *ngIf="password.invalid && (myform.submitted || password.touched)" class="alert alert-danger">
        <div *ngIf="password.errors.required"> Password is required. </div>
        <div *ngIf="password.errors.pattern"> Must contain at least one number and one uppercase and lowercase letter,
            and at least 8 or more characters.</div>
    </div>
</div>

<div class="form-group">
    <input type="password" [(ngModel)]="userdata.confirmpassword" name="confirmpassword" placeholder="Confirm Password"
        class="form-control" required #confirmpassword="ngModel" pattern="{{ password.value }}" />
    <div *ngIf=" confirmpassword.invalid && (myform.submitted || confirmpassword.touched)" class="alert alert-danger">
        <div *ngIf="confirmpassword.errors.required"> Confirm password is required. </div>
        <div *ngIf="confirmpassword.errors.pattern"> Password & Confirm Password does not match.</div>
    </div>
</div>

This question could be solved with a combination of these two answers: https://stackoverflow.com/a/43493648/6294072 and https://stackoverflow.com/a/47670892/6294072

So first of all, you would need a custom validator for checking the passwords, that could look like this:

checkPasswords: ValidatorFn = (group: AbstractControl):  ValidationErrors | null => { 
  let pass = group.get('password').value;
  let confirmPass = group.get('confirmPassword').value
  return pass === confirmPass ? null : { notSame: true }
}

and you would create a formgroup for your fields, instead of just two form controls, then mark that custom validator for your form group:

this.myForm = this.fb.group({
  password: ['', [Validators.required]],
  confirmPassword: ['']
}, { validators: this.checkPasswords })

and then as mentioned in other answer, the mat-error only shows if a FormControl is invalid, so you need an error state matcher:

export class MyErrorStateMatcher implements ErrorStateMatcher {
  isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
    const invalidCtrl = !!(control?.invalid && control?.parent?.dirty);
    const invalidParent = !!(control?.parent?.invalid && control?.parent?.dirty);

    return invalidCtrl || invalidParent;
  }
}

in the above you can tweak when to show error message. I would only show message when the password field is touched. Also I would like above, remove the required validator from the confirmPassword field, since the form is not valid anyway if passwords do not match.

Then in component, create a new ErrorStateMatcher:

matcher = new MyErrorStateMatcher();

Finally, the template would look like this:

<form [formGroup]="myForm">
  <mat-form-field>
    <input matInput placeholder="New password" formControlName="password" required>
    <mat-error *ngIf="myForm.hasError('required', 'password')">
      Please enter your new password
    </mat-error>
  </mat-form-field>

  <mat-form-field>
    <input matInput placeholder="Confirm password" formControlName="confirmPassword" [errorStateMatcher]="matcher">
    <mat-error *ngIf="myForm.hasError('notSame')">
      Passwords do not match
    </mat-error>  
  </mat-form-field>
</form>

Here's a demo for you with the above code: StackBlitz