Disabled field
+
+ Label
+
+ Required field
diff --git a/src/material-experimental/mdc-form-field/BUILD.bazel b/src/material-experimental/mdc-form-field/BUILD.bazel
index d7264e2b63a3..9c0f48573354 100644
--- a/src/material-experimental/mdc-form-field/BUILD.bazel
+++ b/src/material-experimental/mdc-form-field/BUILD.bazel
@@ -24,7 +24,6 @@ ng_module(
"//src/material/core",
"//src/material/form-field",
"@npm//@angular/forms",
- "@npm//@material/floating-label",
"@npm//@material/line-ripple",
"@npm//@material/textfield",
"@npm//rxjs",
diff --git a/src/material-experimental/mdc-form-field/_mdc-text-field-structure-overrides.scss b/src/material-experimental/mdc-form-field/_mdc-text-field-structure-overrides.scss
index efd0c5b31be7..d0ba77ce49bc 100644
--- a/src/material-experimental/mdc-form-field/_mdc-text-field-structure-overrides.scss
+++ b/src/material-experimental/mdc-form-field/_mdc-text-field-structure-overrides.scss
@@ -6,16 +6,6 @@
// styles to fit our needs. See individual comments for context on why
// certain MDC styles need to be modified.
@mixin _mat-mdc-text-field-structure-overrides() {
- // Always hide the asterisk displayed by MDC. This is necessary because MDC can only display
- // the asterisk if the label is directly preceded by the input. MDC does this because it
- // relies on CSS to figure out if the input/textarea is required. This does not apply for us
- // because it's not guaranteed that the form control is an input/textarea. The required state
- // is handled as part of the registered form-field control instance. The asterisk will be
- // rendered conditionally through the floating label.
- .mat-mdc-form-field .mdc-floating-label::after {
- display: none;
- }
-
// Unset the border set by MDC. We move the border (which serves as the Material Design
// text-field bottom line) into its own element. This is necessary because we want the
// bottom-line to span across the whole form-field (including prefixes and suffixes). Also
diff --git a/src/material-experimental/mdc-form-field/directives/floating-label.ts b/src/material-experimental/mdc-form-field/directives/floating-label.ts
index 7468961c6603..e0932abb3d3b 100644
--- a/src/material-experimental/mdc-form-field/directives/floating-label.ts
+++ b/src/material-experimental/mdc-form-field/directives/floating-label.ts
@@ -6,40 +6,41 @@
* found in the LICENSE file at https://angular.io/license
*/
-import {Directive, ElementRef, Input, OnDestroy} from '@angular/core';
-import {MDCFloatingLabel} from '@material/floating-label';
+import {Directive, ElementRef, Input} from '@angular/core';
+import {ponyfill} from '@material/dom';
/**
- * Internal directive that creates an instance of the MDC floating label
- * component. Using a directive allows us to conditionally render a floating label
- * in the template without having to manually instantiate the `MDCFloatingLabel` component.
+ * Internal directive that maintains a MDC floating label. This directive does not
+ * use the `MDCFloatingLabelFoundation` class, as it is not worth the size cost of
+ * including it just to measure the label width and toggle some classes.
*
- * The component is responsible for setting up the floating label styles, and for providing
- * an @Input that can be used by the form-field to toggle floating state of the label.
+ * The use of a directive allows us to conditionally render a floating label in the
+ * template without having to manually manage instantiation and destruction of the
+ * floating label component based on.
+ *
+ * The component is responsible for setting up the floating label styles, measuring label
+ * width for the outline notch, and providing inputs that can be used to toggle the
+ * label's floating or required state.
*/
@Directive({
selector: 'label[matFormFieldFloatingLabel]',
host: {
'class': 'mdc-floating-label',
+ '[class.mdc-floating-label--required]': 'required',
+ '[class.mdc-floating-label--float-above]': 'floating',
},
})
-export class MatFormFieldFloatingLabel extends MDCFloatingLabel implements OnDestroy {
- @Input()
- get floating() { return this._floating; }
- set floating(shouldFloat: boolean) {
- if (shouldFloat !== this._floating) {
- this._floating = shouldFloat;
- this.float(shouldFloat);
- }
- }
- private _floating = false;
+export class MatFormFieldFloatingLabel {
+ /** Whether the label is floating. */
+ @Input() floating: boolean = false;
+ /** Whether the label is required. */
+ @Input() required: boolean = false;
- constructor(private _elementRef: ElementRef) {
- super(_elementRef.nativeElement);
- }
+ constructor(private _elementRef: ElementRef) {}
- ngOnDestroy() {
- this.destroy();
+ /** Gets the width of the label. Used for the outline notch. */
+ getWidth(): number {
+ return ponyfill.estimateScrollWidth(this._elementRef.nativeElement);
}
/** Gets the HTML element for the floating label. */
diff --git a/src/material-experimental/mdc-form-field/form-field.html b/src/material-experimental/mdc-form-field/form-field.html
index 595d94629e78..55d54f3c43b3 100644
--- a/src/material-experimental/mdc-form-field/form-field.html
+++ b/src/material-experimental/mdc-form-field/form-field.html
@@ -14,7 +14,9 @@
*Note*: We add aria-owns as a workaround for an issue in JAWS & NVDA where the label isn't
read if it comes before the control in the DOM.
-->
-
diff --git a/src/material-experimental/mdc-form-field/form-field.ts b/src/material-experimental/mdc-form-field/form-field.ts
index b8c7eb7b6223..5f7e6546f28f 100644
--- a/src/material-experimental/mdc-form-field/form-field.ts
+++ b/src/material-experimental/mdc-form-field/form-field.ts
@@ -244,9 +244,10 @@ export class MatFormField implements AfterViewInit, OnDestroy, AfterContentCheck
// want to update the notch whenever the `_shouldLabelFloat()` value changes.
getLabelWidth: () => 0,
- // TODO: MDC now supports setting the required asterisk marker directly on
- // the label component. This adapter method may be implemented and
- // mat-mdc-form-field-required-marker removed.
+ // We don't use `setLabelRequired` as it relies on a mutation observer for determining
+ // when the `required` state changes. This is not reliable and flexible enough for
+ // our form field, as we support custom controls and detect the required state through
+ // a public property in the abstract form control.
setLabelRequired: () => {},
notchOutline: () => {},
closeOutline: () => {},
diff --git a/src/material-experimental/mdc-form-field/testing/BUILD.bazel b/src/material-experimental/mdc-form-field/testing/BUILD.bazel
index 2a43cdd7a25c..50bea3cd6769 100644
--- a/src/material-experimental/mdc-form-field/testing/BUILD.bazel
+++ b/src/material-experimental/mdc-form-field/testing/BUILD.bazel
@@ -50,7 +50,7 @@ ng_web_test_suite(
"@npm//:node_modules/@material/textfield/dist/mdc.textfield.js",
"@npm//:node_modules/@material/line-ripple/dist/mdc.lineRipple.js",
"@npm//:node_modules/@material/notched-outline/dist/mdc.notchedOutline.js",
- "@npm//:node_modules/@material/floating-label/dist/mdc.floatingLabel.js",
+ "@npm//:node_modules/@material/dom/dist/mdc.dom.js",
],
deps = [
":unit_tests_lib",
diff --git a/src/material-experimental/mdc-input/BUILD.bazel b/src/material-experimental/mdc-input/BUILD.bazel
index 53c2c3c390a8..89bbacd425f8 100644
--- a/src/material-experimental/mdc-input/BUILD.bazel
+++ b/src/material-experimental/mdc-input/BUILD.bazel
@@ -60,10 +60,10 @@ ng_test_library(
ng_web_test_suite(
name = "unit_tests",
static_files = [
+ "@npm//:node_modules/@material/dom/dist/mdc.dom.js",
"@npm//:node_modules/@material/textfield/dist/mdc.textfield.js",
"@npm//:node_modules/@material/line-ripple/dist/mdc.lineRipple.js",
"@npm//:node_modules/@material/notched-outline/dist/mdc.notchedOutline.js",
- "@npm//:node_modules/@material/floating-label/dist/mdc.floatingLabel.js",
],
deps = [
":input_tests_lib",
diff --git a/src/material-experimental/mdc-input/input.spec.ts b/src/material-experimental/mdc-input/input.spec.ts
index d03328246cd0..178b71132b23 100644
--- a/src/material-experimental/mdc-input/input.spec.ts
+++ b/src/material-experimental/mdc-input/input.spec.ts
@@ -315,48 +315,41 @@ describe('MatMdcInput without forms', () => {
}));
it('supports label required star', fakeAsync(() => {
- let fixture = createComponent(MatInputLabelRequiredTestComponent);
+ const fixture = createComponent(MatInputLabelRequiredTestComponent);
fixture.detectChanges();
- let el = fixture.debugElement.query(By.css('label'))!;
- expect(el).not.toBeNull();
- expect(el.nativeElement.textContent).toBe('hello *');
+ const label = fixture.debugElement.query(By.css('label'))!;
+ expect(label).not.toBeNull();
+ expect(label.nativeElement.textContent).toBe('hello');
+ expect(label.nativeElement.classList).toContain('mdc-floating-label--required');
}));
- it('should hide the required star if input is disabled', () => {
+ it('should not hide the required star if input is disabled', () => {
const fixture = createComponent(MatInputLabelRequiredTestComponent);
fixture.componentInstance.disabled = true;
fixture.detectChanges();
- const el = fixture.debugElement.query(By.css('label'))!;
-
- expect(el).not.toBeNull();
- expect(el.nativeElement.textContent).toBe('hello');
+ const label = fixture.debugElement.query(By.css('label'))!;
+ expect(label).not.toBeNull();
+ expect(label.nativeElement.textContent).toBe('hello');
+ expect(label.nativeElement.classList).toContain('mdc-floating-label--required');
});
- it('should hide the required star from screen readers', fakeAsync(() => {
- let fixture = createComponent(MatInputLabelRequiredTestComponent);
- fixture.detectChanges();
-
- let el = fixture.debugElement
- .query(By.css('.mat-mdc-form-field-required-marker'))!.nativeElement;
-
- expect(el.getAttribute('aria-hidden')).toBe('true');
- }));
-
it('hide label required star when set to hide the required marker', fakeAsync(() => {
- let fixture = createComponent(MatInputLabelRequiredTestComponent);
+ const fixture = createComponent(MatInputLabelRequiredTestComponent);
fixture.detectChanges();
- let el = fixture.debugElement.query(By.css('label'))!;
- expect(el).not.toBeNull();
- expect(el.nativeElement.textContent).toBe('hello *');
+ const label = fixture.debugElement.query(By.css('label'))!;
+ expect(label).not.toBeNull();
+ expect(label.nativeElement.classList).toContain('mdc-floating-label--required');
+ expect(label.nativeElement.textContent).toBe('hello');
fixture.componentInstance.hideRequiredMarker = true;
fixture.detectChanges();
- expect(el.nativeElement.textContent).toBe('hello');
+ expect(label.nativeElement.classList).not.toContain('mdc-floating-label--required');
+ expect(label.nativeElement.textContent).toBe('hello');
}));
it('supports the disabled attribute as binding', fakeAsync(() => {
diff --git a/src/material-experimental/mdc-input/testing/BUILD.bazel b/src/material-experimental/mdc-input/testing/BUILD.bazel
index ea5662001bb2..8f48869fe2d6 100644
--- a/src/material-experimental/mdc-input/testing/BUILD.bazel
+++ b/src/material-experimental/mdc-input/testing/BUILD.bazel
@@ -39,7 +39,7 @@ ng_web_test_suite(
"@npm//:node_modules/@material/textfield/dist/mdc.textfield.js",
"@npm//:node_modules/@material/line-ripple/dist/mdc.lineRipple.js",
"@npm//:node_modules/@material/notched-outline/dist/mdc.notchedOutline.js",
- "@npm//:node_modules/@material/floating-label/dist/mdc.floatingLabel.js",
+ "@npm//:node_modules/@material/dom/dist/mdc.dom.js",
],
deps = [
":unit_tests_lib",