diff --git a/src/material/form-field/directives/error.ts b/src/material/form-field/directives/error.ts index ab56e375f76c..f66204c1f0f7 100644 --- a/src/material/form-field/directives/error.ts +++ b/src/material/form-field/directives/error.ts @@ -6,7 +6,14 @@ * found in the LICENSE file at https://angular.dev/license */ -import {Directive, InjectionToken, Input, inject} from '@angular/core'; +import { + Directive, + ElementRef, + HostAttributeToken, + InjectionToken, + Input, + inject, +} from '@angular/core'; import {_IdGenerator} from '@angular/cdk/a11y'; /** @@ -21,10 +28,24 @@ export const MAT_ERROR = new InjectionToken('MatError'); selector: 'mat-error, [matError]', host: { 'class': 'mat-mdc-form-field-error mat-mdc-form-field-bottom-align', + 'aria-atomic': 'true', '[id]': 'id', }, providers: [{provide: MAT_ERROR, useExisting: MatError}], }) export class MatError { @Input() id: string = inject(_IdGenerator).getId('mat-mdc-error-'); + + constructor(...args: unknown[]); + + constructor() { + const ariaLive = inject(new HostAttributeToken('aria-live'), {optional: true}); + + // If no aria-live value is set add 'polite' as a default. This is preferred over setting + // role='alert' so that screen readers do not interrupt the current task to read this aloud. + if (!ariaLive) { + const elementRef = inject(ElementRef); + elementRef.nativeElement.setAttribute('aria-live', 'polite'); + } + } } diff --git a/src/material/form-field/form-field.html b/src/material/form-field/form-field.html index b5e38afad4fb..73f3ea5847e9 100644 --- a/src/material/form-field/form-field.html +++ b/src/material/form-field/form-field.html @@ -95,7 +95,7 @@ } -
diff --git a/src/material/input/input.spec.ts b/src/material/input/input.spec.ts index 27a3c8dbfa0a..1536d8f7ce08 100644 --- a/src/material/input/input.spec.ts +++ b/src/material/input/input.spec.ts @@ -1276,13 +1276,11 @@ describe('MatInput with forms', () => { .toBe(1); }); - it('should be in a parent element with the an aria-live attribute to announce the error', () => { + it('should set aria-live on the error to announce it', () => { testComponent.formControl.markAsTouched(); fixture.detectChanges(); - expect( - containerEl.querySelector('[aria-live]:has(mat-error)')!.getAttribute('aria-live'), - ).toBe('polite'); + expect(containerEl.querySelector('mat-error')!.getAttribute('aria-live')).toBe('polite'); }); it('sets the aria-describedby to reference errors when in error state', () => {