Skip to content

Commit d8e4bc6

Browse files
committed
fixup! fix(material/input): do not override existing aria-describedby value
Add documentation
1 parent 3609e75 commit d8e4bc6

File tree

3 files changed

+26
-11
lines changed

3 files changed

+26
-11
lines changed

guides/creating-a-custom-form-field-control.md

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -339,15 +339,30 @@ controlType = 'example-tel-input';
339339

340340
#### `setDescribedByIds(ids: string[])`
341341

342-
This method is used by the `<mat-form-field>` to specify the IDs that should be used for the
343-
`aria-describedby` attribute of your component. The method has one parameter, the list of IDs, we
344-
just need to apply the given IDs to our host element.
342+
This method is used by the `<mat-form-field>` to set element ids that should be used for the
343+
`aria-describedby` attribute of your control. The ids are controlled trough the form field
344+
as hints or errors are conditionally displayed and should be reflected in the control's
345+
`aria-describedby` attribute for an improved accessibility experience.
346+
347+
The `setDescribedByIds` method is invoked whenever the control's state changes. Custom controls
348+
need to implement this method and update the `aria-describedby` attribute based on the specified
349+
element ids. Below is an example that shows how this can be achieved.
350+
351+
Note that the method by default will not respect element ids that have been set manually on the
352+
control element through the `aria-describedby` attribute. To ensure that your control does not
353+
accidentally override existing element ids specified by consumers of your control, create an
354+
input called `userAriaDescribedby` like followed:
345355

346356
```ts
347-
@HostBinding('attr.aria-describedby') describedBy = '';
357+
@Input('aria-describedby') userAriaDescribedBy: string;
358+
```
348359

360+
The form field will then pick up the user specified `aria-describedby` ids and merge
361+
them with the ids from hints or errors whenever `setDescribedByIds` is invoked.
362+
363+
```ts
349364
setDescribedByIds(ids: string[]) {
350-
this.describedBy = ids.join(' ');
365+
this._elementRef.nativeElement.setAttribute('aria-describedby', ids.join(' '));
351366
}
352367
```
353368

src/components-examples/material-experimental/mdc-form-field/mdc-form-field-custom-control/form-field-custom-control-example.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ export class MyTel {
2727
host: {
2828
'[class.example-floating]': 'shouldLabelFloat',
2929
'[id]': 'id',
30-
'[attr.aria-describedby]': 'describedBy',
3130
}
3231
})
3332
export class MyTelInput implements ControlValueAccessor, MatFormFieldControl<MyTel>, OnDestroy {
@@ -39,7 +38,6 @@ export class MyTelInput implements ControlValueAccessor, MatFormFieldControl<MyT
3938
errorState = false;
4039
controlType = 'example-tel-input';
4140
id = `example-tel-input-${MyTelInput.nextId++}`;
42-
describedBy = '';
4341
onChange = (_: any) => {};
4442
onTouched = () => {};
4543

@@ -51,6 +49,8 @@ export class MyTelInput implements ControlValueAccessor, MatFormFieldControl<MyT
5149

5250
get shouldLabelFloat() { return this.focused || !this.empty; }
5351

52+
@Input('aria-describedby') userAriaDescribedBy: string;
53+
5454
@Input()
5555
get placeholder(): string { return this._placeholder; }
5656
set placeholder(value: string) {
@@ -121,7 +121,7 @@ export class MyTelInput implements ControlValueAccessor, MatFormFieldControl<MyT
121121
}
122122

123123
setDescribedByIds(ids: string[]) {
124-
this.describedBy = ids.join(' ');
124+
this._elementRef.nativeElement.setAttribute('aria-describedby', ids.join(' '));
125125
}
126126

127127
onContainerClick(event: MouseEvent) {

src/components-examples/material/form-field/form-field-custom-control/form-field-custom-control-example.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ export class MyTel {
5151
host: {
5252
'[class.example-floating]': 'shouldLabelFloat',
5353
'[id]': 'id',
54-
'[attr.aria-describedby]': 'describedBy'
5554
}
5655
})
5756
export class MyTelInput
@@ -67,7 +66,6 @@ export class MyTelInput
6766
errorState = false;
6867
controlType = 'example-tel-input';
6968
id = `example-tel-input-${MyTelInput.nextId++}`;
70-
describedBy = '';
7169
onChange = (_: any) => {};
7270
onTouched = () => {};
7371

@@ -83,6 +81,8 @@ export class MyTelInput
8381
return this.focused || !this.empty;
8482
}
8583

84+
@Input('aria-describedby') userAriaDescribedBy: string;
85+
8686
@Input()
8787
get placeholder(): string {
8888
return this._placeholder;
@@ -182,7 +182,7 @@ export class MyTelInput
182182
}
183183

184184
setDescribedByIds(ids: string[]) {
185-
this.describedBy = ids.join(' ');
185+
this._elementRef.nativeElement.setAttribute('aria-describedby', ids.join(' '));
186186
}
187187

188188
onContainerClick(event: MouseEvent) {

0 commit comments

Comments
 (0)