diff --git a/src/dev-app/input/input-demo.html b/src/dev-app/input/input-demo.html
index d41d02055512..3fbdf726be63 100644
--- a/src/dev-app/input/input-demo.html
+++ b/src/dev-app/input/input-demo.html
@@ -880,6 +880,41 @@
Custom control
+
+ Without label
+
+ Label removed
+
+
+ @if (hasLabel$ | async){
+ My input
+ }
+
+
+
+
+
+ @if (hasLabel$ | async){
+ My input
+ }
+
+
+
+ No defined label
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/dev-app/input/input-demo.ts b/src/dev-app/input/input-demo.ts
index 36caf31c80e0..49d5af853834 100644
--- a/src/dev-app/input/input-demo.ts
+++ b/src/dev-app/input/input-demo.ts
@@ -29,6 +29,7 @@ import {MatIconModule} from '@angular/material/icon';
import {MatTabsModule} from '@angular/material/tabs';
import {MatToolbarModule} from '@angular/material/toolbar';
import {MatTooltipModule} from '@angular/material/tooltip';
+import {BehaviorSubject} from 'rxjs';
let max = 5;
@@ -100,8 +101,13 @@ export class InputDemo {
fillAppearance: string;
outlineAppearance: string;
+ hasLabel$ = new BehaviorSubject(true);
+
constructor() {
- setTimeout(() => this.delayedFormControl.setValue('hello'), 100);
+ setTimeout(() => {
+ this.delayedFormControl.setValue('hello');
+ this.hasLabel$.next(false);
+ }, 100);
}
addABunch(n: number) {
diff --git a/src/material/form-field/form-field.ts b/src/material/form-field/form-field.ts
index 13f6f13cab70..914f89a32d2e 100644
--- a/src/material/form-field/form-field.ts
+++ b/src/material/form-field/form-field.ts
@@ -555,7 +555,10 @@ export class MatFormField
_hasFloatingLabel = computed(() => !!this._labelChild());
- _shouldLabelFloat() {
+ _shouldLabelFloat(): boolean {
+ if (!this._hasFloatingLabel()) {
+ return false;
+ }
return this._control.shouldLabelFloat || this._shouldAlwaysFloat();
}
diff --git a/src/material/input/input.spec.ts b/src/material/input/input.spec.ts
index 7e51bfa7aa8b..4158c0162001 100644
--- a/src/material/input/input.spec.ts
+++ b/src/material/input/input.spec.ts
@@ -157,7 +157,8 @@ describe('MatMdcInput without forms', () => {
fixture.detectChanges();
expect(formField._control.empty).toBe(false);
- expect(formField._shouldLabelFloat()).toBe(true);
+ // should not float label if there is no label
+ expect(formField._shouldLabelFloat()).toBe(false);
}));
it('should not be empty when the value set before view init', fakeAsync(() => {
@@ -1531,6 +1532,62 @@ describe('MatFormField default options', () => {
).toBe(true);
});
});
+describe('MatFormField without label', () => {
+ it('should not float the label when no label is defined.', () => {
+ let fixture = createComponent(MatInputWithoutDefinedLabel);
+ fixture.detectChanges();
+
+ const inputEl = fixture.debugElement.query(By.css('input'))!;
+ const formField = fixture.debugElement.query(By.directive(MatFormField))!
+ .componentInstance as MatFormField;
+
+ // Update the value of the input and set focus.
+ inputEl.nativeElement.value = 'Text';
+ fixture.detectChanges();
+
+ // should not float label if there is no label
+ expect(formField._shouldLabelFloat()).toBe(false);
+ });
+
+ it('should not float the label when the label is removed after it has been shown', () => {
+ let fixture = createComponent(MatInputWithCondictionalLabel);
+ fixture.detectChanges();
+ const inputEl = fixture.debugElement.query(By.css('input'))!;
+ const formField = fixture.debugElement.query(By.directive(MatFormField))!
+ .componentInstance as MatFormField;
+
+ // initially, label is present
+ expect(fixture.nativeElement.querySelector('label')).not.toBeNull();
+
+ // removing label after it has been shown
+ fixture.componentInstance.hasLabel = false;
+ inputEl.nativeElement.value = 'Text';
+ fixture.changeDetectorRef.markForCheck();
+ fixture.detectChanges();
+
+ // now expected to not have a label
+ expect(fixture.nativeElement.querySelector('label')).toBeNull();
+ // should not float label since there is no label
+ expect(formField._shouldLabelFloat()).toBe(false);
+ });
+
+ it('should float the label when the label is not removed', () => {
+ let fixture = createComponent(MatInputWithCondictionalLabel);
+ fixture.detectChanges();
+ const inputEl = fixture.debugElement.query(By.css('input'))!;
+ const formField = fixture.debugElement.query(By.directive(MatFormField))!
+ .componentInstance as MatFormField;
+
+ inputEl.nativeElement.value = 'Text';
+ fixture.changeDetectorRef.markForCheck();
+ fixture.detectChanges();
+
+ // Expected to have a label
+ expect(fixture.nativeElement.querySelector('label')).not.toBeNull();
+ // should float label since there is a label
+ expect(formField._shouldLabelFloat()).toBe(true);
+ });
+});
function configureTestingModule(
component: Type,
@@ -1787,6 +1844,28 @@ class MatInputWithDynamicLabel {
shouldFloat: 'always' | 'auto' = 'always';
}
+@Component({
+ template: `
+
+
+
+ `,
+})
+class MatInputWithoutDefinedLabel {}
+
+@Component({
+ template: `
+
+ @if (hasLabel) {
+ Label
+ }
+
+ `,
+})
+class MatInputWithCondictionalLabel {
+ hasLabel = true;
+}
+
@Component({
template: `