@@ -36,7 +36,7 @@ import {AbstractControlDirective} from '@angular/forms';
3636import { ThemePalette } from '@angular/material/core' ;
3737import { _IdGenerator } from '@angular/cdk/a11y' ;
3838import { Subject , Subscription , merge } from 'rxjs' ;
39- import { takeUntil } from 'rxjs/operators' ;
39+ import { map , pairwise , takeUntil , filter , startWith } from 'rxjs/operators' ;
4040import { MAT_ERROR , MatError } from './directives/error' ;
4141import {
4242 FLOATING_LABEL_PARENT ,
@@ -328,6 +328,7 @@ export class MatFormField
328328 private _previousControl : MatFormFieldControl < unknown > | null = null ;
329329 private _stateChanges : Subscription | undefined ;
330330 private _valueChanges : Subscription | undefined ;
331+ private _describedByChanges : Subscription | undefined ;
331332
332333 private _injector = inject ( Injector ) ;
333334
@@ -377,6 +378,7 @@ export class MatFormField
377378 ngOnDestroy ( ) {
378379 this . _stateChanges ?. unsubscribe ( ) ;
379380 this . _valueChanges ?. unsubscribe ( ) ;
381+ this . _describedByChanges ?. unsubscribe ( ) ;
380382 this . _destroyed . next ( ) ;
381383 this . _destroyed . complete ( ) ;
382384 }
@@ -426,10 +428,22 @@ export class MatFormField
426428 this . _stateChanges ?. unsubscribe ( ) ;
427429 this . _stateChanges = control . stateChanges . subscribe ( ( ) => {
428430 this . _updateFocusState ( ) ;
429- this . _syncDescribedByIds ( ) ;
430431 this . _changeDetectorRef . markForCheck ( ) ;
431432 } ) ;
432433
434+ // Updating the `aria-describedby` touches the DOM. Only do it if it actually needs to change.
435+ this . _describedByChanges ?. unsubscribe ( ) ;
436+ this . _describedByChanges = control . stateChanges
437+ . pipe (
438+ startWith ( [ undefined , undefined ] as const ) ,
439+ map ( ( ) => [ control . errorState , control . userAriaDescribedBy ] as const ) ,
440+ pairwise ( ) ,
441+ filter ( ( [ [ prevErrorState , prevDescribedBy ] , [ currentErrorState , currentDescribedBy ] ] ) => {
442+ return prevErrorState !== currentErrorState || prevDescribedBy !== currentDescribedBy ;
443+ } ) ,
444+ )
445+ . subscribe ( ( ) => this . _syncDescribedByIds ( ) ) ;
446+
433447 this . _valueChanges ?. unsubscribe ( ) ;
434448
435449 // Run change detection if the value changes.
0 commit comments