Skip to content

Commit cd71944

Browse files
committed
feat(form-field): expose label element id for custom controls
Currently, the form-field always creates a label element as sibling to projected form-field controls. For native controls, the label is associated with the controls using the `for` attribute. This doesn't work for custom controls which might not be based on native controls. e.g. the `mat-select`. In those cases, the appropriate aria attributes need to be applied with `aria-labelledby` that refers to the label content element. Since this is a common pattern for custom controls that don't use native controls, we need to expose the element id for the label content. Currently we already do this for the select, but just prefixed it with an underscore. This denotes it as private API while there is obviously a use-case for exposing this publicly. Best example is how the select _needs_ it.
1 parent 1ba8b26 commit cd71944

File tree

5 files changed

+26
-11
lines changed

5 files changed

+26
-11
lines changed

src/material-experimental/mdc-form-field/form-field.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
*ngIf="_hasFloatingLabel()"
1919
(cdkObserveContent)="_refreshOutlineNotchWidth()"
2020
[cdkObserveContentDisabled]="!_hasOutline()"
21-
[id]="_labelId"
21+
[id]="labelContentId"
2222
[attr.for]="_control.id"
2323
[attr.aria-owns]="_control.id">
2424
<ng-content select="mat-label"></ng-content>

src/material-experimental/mdc-form-field/form-field.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -192,11 +192,11 @@ export class MatFormField implements AfterViewInit, OnDestroy, AfterContentCheck
192192
}
193193
private _hintLabel = '';
194194

195-
// Unique id for the hint label.
196-
_hintLabelId: string = `mat-hint-${nextUniqueId++}`;
195+
// Unique id for the label element.
196+
readonly _labelId: string = `mat-form-field-label-${nextUniqueId++}`;
197197

198-
// Unique id for the internal form field label.
199-
_labelId = `mat-form-field-label-${nextUniqueId++}`;
198+
// Unique id for the hint label.
199+
readonly _hintLabelId: string = `mat-hint-${nextUniqueId++}`;
200200

201201
/** State of the mat-hint and mat-error animations. */
202202
_subscriptAnimationState: string = '';
@@ -349,6 +349,13 @@ export class MatFormField implements AfterViewInit, OnDestroy, AfterContentCheck
349349
this._destroyed.complete();
350350
}
351351

352+
/**
353+
* Gets the id of the label element. If no label is present, returns `null`.
354+
*/
355+
getLabelId(): string|null {
356+
return this._hasFloatingLabel() ? this._labelId : null;
357+
}
358+
352359
/**
353360
* Gets an ElementRef for the element that a overlay attached to the form-field
354361
* should be positioned relative to.

src/material/form-field/form-field.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -210,10 +210,10 @@ export class MatFormField extends _MatFormFieldMixinBase
210210
private _hintLabel = '';
211211

212212
// Unique id for the hint label.
213-
_hintLabelId: string = `mat-hint-${nextUniqueId++}`;
213+
readonly _hintLabelId: string = `mat-hint-${nextUniqueId++}`;
214214

215-
// Unique id for the internal form field label.
216-
_labelId = `mat-form-field-label-${nextUniqueId++}`;
215+
// Unique id for the label element.
216+
readonly _labelId = `mat-form-field-label-${nextUniqueId++}`;
217217

218218
/**
219219
* Whether the label should always float, never float or float as the user types.
@@ -291,6 +291,13 @@ export class MatFormField extends _MatFormFieldMixinBase
291291
_defaults.hideRequiredMarker : false;
292292
}
293293

294+
/**
295+
* Gets the id of the label element. If no label is present, returns `null`.
296+
*/
297+
getLabelId(): string|null {
298+
return this._hasFloatingLabel() ? this._labelId : null;
299+
}
300+
294301
/**
295302
* Gets an ElementRef for the element that a overlay attached to the form-field should be
296303
* positioned relative to.

src/material/select/select.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1166,7 +1166,7 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit,
11661166
return null;
11671167
}
11681168

1169-
return this._parentFormField._labelId || null;
1169+
return this._parentFormField.getLabelId();
11701170
}
11711171

11721172
/** Determines the `aria-activedescendant` to be set on the host. */

tools/public_api_guard/material/form-field.d.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,12 @@ export declare class MatFormField extends _MatFormFieldMixinBase implements Afte
2626
_elementRef: ElementRef;
2727
_errorChildren: QueryList<MatError>;
2828
_hintChildren: QueryList<MatHint>;
29-
_hintLabelId: string;
29+
readonly _hintLabelId: string;
3030
_inputContainerRef: ElementRef;
3131
get _labelChild(): MatLabel;
3232
_labelChildNonStatic: MatLabel;
3333
_labelChildStatic: MatLabel;
34-
_labelId: string;
34+
readonly _labelId: string;
3535
_placeholderChild: MatPlaceholder;
3636
_prefixChildren: QueryList<MatPrefix>;
3737
get _shouldAlwaysFloat(): boolean;
@@ -57,6 +57,7 @@ export declare class MatFormField extends _MatFormFieldMixinBase implements Afte
5757
_shouldLabelFloat(): boolean;
5858
protected _validateControlChild(): void;
5959
getConnectedOverlayOrigin(): ElementRef;
60+
getLabelId(): string | null;
6061
ngAfterContentChecked(): void;
6162
ngAfterContentInit(): void;
6263
ngAfterViewInit(): void;

0 commit comments

Comments
 (0)