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: `