Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions goldens/material/form-field/index.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@ import * as i2 from '@angular/cdk/bidi';
import { InjectionToken } from '@angular/core';
import { NgControl } from '@angular/forms';
import { Observable } from 'rxjs';
import { OnChanges } from '@angular/core';
import { OnDestroy } from '@angular/core';
import { QueryList } from '@angular/core';
import { SimpleChanges } from '@angular/core';

// @public
export type FloatLabelType = 'always' | 'auto';
Expand Down
1 change: 1 addition & 0 deletions goldens/material/input/index.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { OnChanges } from '@angular/core';
import { OnDestroy } from '@angular/core';
import { Platform } from '@angular/cdk/platform';
import { QueryList } from '@angular/core';
import { SimpleChanges } from '@angular/core';
import { Subject } from 'rxjs';
import { WritableSignal } from '@angular/core';

Expand Down
31 changes: 25 additions & 6 deletions src/material/form-field/directives/notched-outline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import {
ElementRef,
Input,
NgZone,
OnChanges,
SimpleChanges,
ViewChild,
ViewEncapsulation,
inject,
Expand All @@ -36,21 +38,27 @@ import {
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
})
export class MatFormFieldNotchedOutline implements AfterViewInit {
export class MatFormFieldNotchedOutline implements AfterViewInit, OnChanges {
private _elementRef = inject<ElementRef<HTMLElement>>(ElementRef);
private _ngZone = inject(NgZone);

/** Whether the notch should be opened. */
@Input('matFormFieldNotchedOutlineOpen') open: boolean = false;

/** Whether the floating label is present. */
@Input('matFormFieldHasFloatingLabel') hasFloatingLabel: boolean = false;

@ViewChild('notch') _notch: ElementRef<HTMLElement>;

ngAfterViewInit(): void {
const element = this._elementRef.nativeElement;
const label = element.querySelector<HTMLElement>('.mdc-floating-label');
/** Gets the HTML element for the floating label. */
get element(): HTMLElement {
return this._elementRef.nativeElement;
}

ngAfterViewInit(): void {
const label = this.element.querySelector<HTMLElement>('.mdc-floating-label');
if (label) {
element.classList.add('mdc-notched-outline--upgraded');
this.element.classList.add('mdc-notched-outline--upgraded');

if (typeof requestAnimationFrame === 'function') {
label.style.transitionDuration = '0s';
Expand All @@ -59,7 +67,18 @@ export class MatFormFieldNotchedOutline implements AfterViewInit {
});
}
} else {
element.classList.add('mdc-notched-outline--no-label');
this.element.classList.add('mdc-notched-outline--no-label');
}
}

ngOnChanges(changes: SimpleChanges) {
if (
changes['hasFloatingLabel'] &&
this.hasFloatingLabel &&
this.element.classList.contains('mdc-notched-outline--no-label')
) {
this.element.classList.add('mdc-notched-outline--upgraded');
this.element.classList.remove('mdc-notched-outline--no-label');
}
}

Expand Down
5 changes: 4 additions & 1 deletion src/material/form-field/form-field.html
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,10 @@
}
<div class="mat-mdc-form-field-flex">
@if (_hasOutline()) {
<div matFormFieldNotchedOutline [matFormFieldNotchedOutlineOpen]="_shouldLabelFloat()">
<div
matFormFieldNotchedOutline
[matFormFieldNotchedOutlineOpen]="_shouldLabelFloat()"
[matFormFieldHasFloatingLabel]="_hasFloatingLabel()">
@if (!_forceDisplayInfixLabel()) {
<ng-template [ngTemplateOutlet]="labelTemplate"></ng-template>
}
Expand Down
37 changes: 37 additions & 0 deletions src/material/input/input.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,27 @@ describe('MatMdcInput without forms', () => {
expect(input.getAttribute('aria-describedby')).toBe(`initial ${hintId}`);
}));

it('should show outline label correctly based on initial condition to false', fakeAsync(() => {
const fixture = TestBed.createComponent(MatInputOutlineWithConditionalLabel);
fixture.detectChanges();
tick(16);

const notchedOutline: HTMLElement = fixture.debugElement.query(
By.css('.mdc-notched-outline'),
).nativeElement;

expect(notchedOutline.classList).toContain('mdc-notched-outline--no-label');
expect(notchedOutline.classList).not.toContain('mdc-notched-outline--upgraded');

fixture.componentInstance.showLabel = true;
fixture.changeDetectorRef.markForCheck();
fixture.detectChanges();
tick(16);

expect(notchedOutline.classList).not.toContain('mdc-notched-outline--no-label');
expect(notchedOutline.classList).toContain('mdc-notched-outline--upgraded');
}));

it('supports user binding to aria-describedby', fakeAsync(() => {
const fixture = TestBed.createComponent(MatInputWithSubscriptAndAriaDescribedBy);

Expand Down Expand Up @@ -2170,6 +2191,22 @@ class MatInputWithAppearance {
appearance: MatFormFieldAppearance;
}

@Component({
template: `
<mat-form-field appearance="outline">
@if(showLabel) {
<mat-label>My Label</mat-label>
}
<input matInput placeholder="Placeholder">
</mat-form-field>
`,
imports: [MatInputModule],
})
class MatInputOutlineWithConditionalLabel {
@ViewChild(MatFormField) formField: MatFormField;
showLabel: boolean = false;
}

@Component({
template: `
<mat-form-field [subscriptSizing]="sizing">
Expand Down
Loading