Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/components-examples/material/input/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.example-form {
min-width: 150px;
max-width: 500px;
width: 100%;
}

.example-full-width {
width: 100%;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<form class="example-form" [formGroup]="passwordForm">
<mat-form-field class="example-full-width">
<mat-label>Password</mat-label>
<input type="password" matInput formControlName="password">
@if (passwordForm.controls.password.hasError('required')) {
<mat-error>Password is <strong>required</strong></mat-error>
}
</mat-form-field>

<mat-form-field class="example-full-width">
<mat-label>Confirm password</mat-label>
<input type="password" matInput formControlName="confirmPassword" [errorStateMatcher]="matcher">
@if (passwordForm.controls.confirmPassword.hasError('required')) {
<mat-error>Please confirm your password</mat-error>
} @else if (passwordForm.hasError('passwordsMismatch')) {
<mat-error>Passwords don't match</mat-error>
}
</mat-form-field>
</form>
Original file line number Diff line number Diff line change
@@ -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();
}
7 changes: 7 additions & 0 deletions src/material/input/input.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,13 @@ should be shown. (`true` indicating that they should be shown, and `false` indic

<!-- example(input-error-state-matcher) -->

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.

<!-- example(input-parent-form-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.
Expand Down