@@ -23,7 +23,7 @@ class MyTel {
23
23
@Component ({
24
24
selector: ' example-tel-input' ,
25
25
template: `
26
- <div [formGroup]="parts">
26
+ <div role="group" [formGroup]="parts">
27
27
<input class="area" formControlName="area" maxlength="3">
28
28
<span>–</span>
29
29
<input class="exchange" formControlName="exchange" maxlength="3">
@@ -45,7 +45,7 @@ class MyTel {
45
45
}
46
46
` ],
47
47
})
48
- class MyTelInput {
48
+ export class MyTelInput {
49
49
parts: FormGroup ;
50
50
51
51
@Input ()
@@ -85,7 +85,7 @@ a provider to our component so that the form field will be able to inject it as
85
85
...
86
86
providers : [{provide: MatFormFieldControl , useExisting: MyTelInput }],
87
87
})
88
- class MyTelInput implements MatFormFieldControl <MyTel > {
88
+ export class MyTelInput implements MatFormFieldControl <MyTel > {
89
89
...
90
90
}
91
91
```
@@ -201,7 +201,7 @@ To resolve this, remove the `NG_VALUE_ACCESSOR` provider and instead set the val
201
201
// },
202
202
],
203
203
})
204
- class MyTelInput implements MatFormFieldControl <MyTel > {
204
+ export class MyTelInput implements MatFormFieldControl <MyTel > {
205
205
constructor (
206
206
...,
207
207
@Optional () @Self () public ngControl : NgControl ,
@@ -341,16 +341,20 @@ controlType = 'example-tel-input';
341
341
342
342
This method is used by the ` <mat-form-field> ` to specify the IDs that should be used for the
343
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.
344
+ just need to apply the given IDs to the element that represents our control .
345
345
346
- ``` ts
347
- @HostBinding (' attr.aria-describedby' ) describedBy = ' ' ;
346
+ In our concrete example, these IDs would need to be applied to the group element.
348
347
348
+ ``` ts
349
349
setDescribedByIds (ids : string []) {
350
350
this .describedBy = ids .join (' ' );
351
351
}
352
352
```
353
353
354
+ ``` html
355
+ <div role =" group" [formGroup] =" parts" [attr.aria-describedby] =" describedBy" >
356
+ ```
357
+
354
358
#### ` onContainerClick(event: MouseEvent) `
355
359
356
360
This method will be called when the form field is clicked on. It allows your component to hook in
@@ -366,6 +370,41 @@ onContainerClick(event: MouseEvent) {
366
370
}
367
371
```
368
372
373
+ ### Improving accessibility
374
+
375
+ Our custom form field control consists of multiple inputs that describe segments of a phone
376
+ number. For accessibility purposes, we put those inputs as part of a ` div ` element with
377
+ ` role="group" ` . This ensures that screen reader users can tell that all those inputs belong
378
+ together.
379
+
380
+ One significant piece of information is missing for screen reader users though. They won't be able
381
+ to tell what this input group represents. To improve this, we should add a label for the group
382
+ element using either ` aria-label ` or ` aria-labelledby ` .
383
+
384
+ It's recommended to link the group to the label that is displayed as part of the parent
385
+ ` <mat-form-field> ` . This ensures that explicitly specified labels (using ` <mat-label> ` ) are
386
+ actually used for labelling the control.
387
+
388
+ In our concrete example, we add an attribute binding for ` aria-labelledby ` and bind it
389
+ to the label element id provided by the parent ` <mat-form-field> ` .
390
+
391
+ ``` typescript
392
+ export class MyTelInput implements MatFormFieldControl <MyTel > {
393
+ ...
394
+
395
+ constructor (...
396
+ @Optional () public parentFormField : MatFormField ) {
397
+ ` ` `
398
+
399
+ ` ` ` html
400
+ @Component ({
401
+ selector: ' example-tel-input' ,
402
+ template: `
403
+ <div role="group" [formGroup]="parts"
404
+ [attr.aria-describedby]="describedBy"
405
+ [attr.aria-labelledby]="parentFormField?.getLabelId()">
406
+ ` ` `
407
+
369
408
### Trying it out
370
409
371
410
Now that we ' ve fully implemented the interface, we' re ready to try our component out ! All we need to
0 commit comments