From d32376619b49c5f1f4694a913284cc45e46b5e23 Mon Sep 17 00:00:00 2001 From: RAJEEV2510 Date: Wed, 16 Sep 2026 20:38:49 +0000 Subject: [PATCH] docs(material/input): add example for showing errors from a parent form group Errors from a validator on a parent form group, such as a check that two fields match, are set on the group instead of the input, so `mat-error` doesn't show them by default. Add an example that uses an `ErrorStateMatcher` to show a parent group's error on the related input. Fixes #8513 --- .../material/input/index.ts | 1 + .../input-parent-form-errors-example.css | 9 +++ .../input-parent-form-errors-example.html | 19 ++++++ .../input-parent-form-errors-example.ts | 59 +++++++++++++++++++ src/material/input/input.md | 7 +++ 5 files changed, 95 insertions(+) create mode 100644 src/components-examples/material/input/input-parent-form-errors/input-parent-form-errors-example.css create mode 100644 src/components-examples/material/input/input-parent-form-errors/input-parent-form-errors-example.html create mode 100644 src/components-examples/material/input/input-parent-form-errors/input-parent-form-errors-example.ts diff --git a/src/components-examples/material/input/index.ts b/src/components-examples/material/input/index.ts index 1d9710df9d46..487ea62e823e 100644 --- a/src/components-examples/material/input/index.ts +++ b/src/components-examples/material/input/index.ts @@ -5,5 +5,6 @@ export {InputErrorsSignalFormExample} from './input-errors-signal-form/input-err export {InputFormExample} from './input-form/input-form-example'; export {InputHintExample} from './input-hint/input-hint-example'; export {InputOverviewExample} from './input-overview/input-overview-example'; +export {InputParentFormErrorsExample} from './input-parent-form-errors/input-parent-form-errors-example'; export {InputPrefixSuffixExample} from './input-prefix-suffix/input-prefix-suffix-example'; export {InputHarnessExample} from './input-harness/input-harness-example'; diff --git a/src/components-examples/material/input/input-parent-form-errors/input-parent-form-errors-example.css b/src/components-examples/material/input/input-parent-form-errors/input-parent-form-errors-example.css new file mode 100644 index 000000000000..08fa67536b1f --- /dev/null +++ b/src/components-examples/material/input/input-parent-form-errors/input-parent-form-errors-example.css @@ -0,0 +1,9 @@ +.example-form { + min-width: 150px; + max-width: 500px; + width: 100%; +} + +.example-full-width { + width: 100%; +} diff --git a/src/components-examples/material/input/input-parent-form-errors/input-parent-form-errors-example.html b/src/components-examples/material/input/input-parent-form-errors/input-parent-form-errors-example.html new file mode 100644 index 000000000000..518c871b5b0a --- /dev/null +++ b/src/components-examples/material/input/input-parent-form-errors/input-parent-form-errors-example.html @@ -0,0 +1,19 @@ +
+ + Password + + @if (passwordForm.controls.password.hasError('required')) { + Password is required + } + + + + Confirm password + + @if (passwordForm.controls.confirmPassword.hasError('required')) { + Please confirm your password + } @else if (passwordForm.hasError('passwordsMismatch')) { + Passwords don't match + } + +
diff --git a/src/components-examples/material/input/input-parent-form-errors/input-parent-form-errors-example.ts b/src/components-examples/material/input/input-parent-form-errors/input-parent-form-errors-example.ts new file mode 100644 index 000000000000..36c85bfc8878 --- /dev/null +++ b/src/components-examples/material/input/input-parent-form-errors/input-parent-form-errors-example.ts @@ -0,0 +1,59 @@ +import {Component} from '@angular/core'; +import { + AbstractControl, + FormControl, + FormGroup, + FormGroupDirective, + NgForm, + ReactiveFormsModule, + ValidationErrors, + ValidatorFn, + Validators, +} from '@angular/forms'; +import {ErrorStateMatcher} from '@angular/material/core'; +import {MatFormFieldModule} from '@angular/material/form-field'; +import {MatInputModule} from '@angular/material/input'; + +/** Validates that the `password` and `confirmPassword` controls of a form group match. */ +const passwordsMatchValidator: ValidatorFn = (group: AbstractControl): ValidationErrors | null => { + const password = group.get('password')?.value; + const confirmPassword = group.get('confirmPassword')?.value; + return password === confirmPassword ? null : {passwordsMismatch: true}; +}; + +/** + * Error state matcher that also shows the parent form group's `passwordsMismatch` error + * once the user has interacted with the control or the form has been submitted. + */ +export class PasswordsMismatchErrorStateMatcher implements ErrorStateMatcher { + isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean { + if (!control) { + return false; + } + + const hasInteracted = control.dirty || control.touched || !!form?.submitted; + const hasError = control.invalid || !!control.parent?.hasError('passwordsMismatch'); + return hasInteracted && hasError; + } +} + +/** + * @title Input with errors from a parent form group + */ +@Component({ + selector: 'input-parent-form-errors-example', + templateUrl: 'input-parent-form-errors-example.html', + styleUrl: 'input-parent-form-errors-example.css', + imports: [MatFormFieldModule, MatInputModule, ReactiveFormsModule], +}) +export class InputParentFormErrorsExample { + protected passwordForm = new FormGroup( + { + password: new FormControl('', Validators.required), + confirmPassword: new FormControl('', Validators.required), + }, + {validators: passwordsMatchValidator}, + ); + + protected matcher = new PasswordsMismatchErrorStateMatcher(); +} diff --git a/src/material/input/input.md b/src/material/input/input.md index 5bc65709328c..849b9df37681 100644 --- a/src/material/input/input.md +++ b/src/material/input/input.md @@ -73,6 +73,13 @@ should be shown. (`true` indicating that they should be shown, and `false` indic +Errors from a validator on a parent form group, such as a check that two fields match, are set on +the group rather than on the input. Because the input's own control stays valid, these errors are +not shown by default. To show them, use an `ErrorStateMatcher` that also checks the parent group's +errors. + + + A global error state matcher can be specified by setting the `ErrorStateMatcher` provider. This applies to all inputs. For convenience, `ShowOnDirtyErrorStateMatcher` is available in order to globally cause input errors to show when the input is dirty and invalid.