I found a bug in AJT_82's answer. Since I do not have enough reputation to comment under AJT_82's answer, I have to post the bug and solution in this answer.
Here is the bug:
Solution: In the following code:
export class MyErrorStateMatcher implements ErrorStateMatcher {
isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
const invalidCtrl = !!(control && control.invalid && control.parent.dirty);
const invalidParent = !!(control && control.parent && control.parent.invalid && control.parent.dirty);
return (invalidCtrl || invalidParent);
}
}
Change control.parent.invalid
to control.parent.hasError('notSame')
will solve this problem.
After the small changes, the problem solved.
Edit: To validate the Confirm Password field only after the user starts typing you can return this instead
return ((invalidCtrl || invalidParent) && control.valid);