@@ -42,7 +42,11 @@ import {
4242 MatFormFieldControl ,
4343} from '@angular/material/form-field' ;
4444import { ANIMATION_MODULE_TYPE } from '@angular/platform-browser/animations' ;
45- import { MDCTextFieldAdapter , MDCTextFieldFoundation } from '@material/textfield' ;
45+ import {
46+ MDCTextFieldAdapter ,
47+ MDCTextFieldFoundation ,
48+ numbers as mdcTextFieldNumbers
49+ } from '@material/textfield' ;
4650import { merge , Subject } from 'rxjs' ;
4751import { takeUntil } from 'rxjs/operators' ;
4852import { MatError } from './directives/error' ;
@@ -197,6 +201,9 @@ export class MatFormField implements AfterViewInit, OnDestroy, AfterContentCheck
197201 /** State of the mat-hint and mat-error animations. */
198202 _subscriptAnimationState : string = '' ;
199203
204+ /** Width of the outline notch. */
205+ _outlineNotchWidth : number ;
206+
200207 /** Gets the current form field control */
201208 get _control ( ) : MatFormFieldControl < any > {
202209 return this . _explicitFormFieldControl || this . _formFieldControl ;
@@ -229,16 +236,24 @@ export class MatFormField implements AfterViewInit, OnDestroy, AfterContentCheck
229236 // TODO(devversion): explore options on how to integrate label shaking.
230237 shakeLabel : ( ) => { } ,
231238
232- getLabelWidth : ( ) => this . _floatingLabel ? this . _floatingLabel . getWidth ( ) : 0 ,
233- notchOutline : labelWidth => this . _notchedOutline && this . _notchedOutline . notch ( labelWidth ) ,
234- closeOutline : ( ) => this . _notchedOutline && this . _notchedOutline . closeNotch ( ) ,
239+ // MDC by default updates the notched-outline whenever the text-field receives focus, or
240+ // is being blurred. It also computes the label width every time the notch is opened or
241+ // closed. This works fine in the standard MDC text-field, but not in Angular where the
242+ // floating label could change through interpolation. We want to be able to update the
243+ // notched outline whenever the label content changes. Additionally, relying on focus or
244+ // blur to open and close the notch does not work for us since abstract form-field controls
245+ // have the ability to control the floating label state (i.e. `shouldLabelFloat`), and we
246+ // want to update the notch whenever the `_shouldLabelFloat()` value changes.
247+ getLabelWidth : ( ) => 0 ,
248+ notchOutline : ( ) => { } ,
249+ closeOutline : ( ) => { } ,
235250
236251 activateLineRipple : ( ) => this . _lineRipple && this . _lineRipple . activate ( ) ,
237252 deactivateLineRipple : ( ) => this . _lineRipple && this . _lineRipple . deactivate ( ) ,
238253
239254 // The foundation tries to register events on the input. This is not matching
240255 // our concept of abstract form field controls. We handle each event manually
241- // in "ngDoCheck " based on the form-field control state. The following events
256+ // in "stateChanges " based on the form-field control state. The following events
242257 // need to be handled: focus, blur. We do not handle the "input" event since
243258 // that one is only needed for the text-field character count, which we do
244259 // not implement as part of the form-field, but should be implemented manually
@@ -310,8 +325,9 @@ export class MatFormField implements AfterViewInit, OnDestroy, AfterContentCheck
310325 // Initial focus state sync. This happens rarely, but we want to account for
311326 // it in case the form-field control has "focused" set to true on init.
312327 this . _updateFocusState ( ) ;
313- // Initial notch update since we overwrote the "shouldFloat" getter.
314- this . _rerenderOutlineNotch ( ) ;
328+ // Initial notch width update. This is needed in case the text-field label floats
329+ // on initialization, and renders inside of the notched outline.
330+ this . _refreshOutlineNotchWidth ( ) ;
315331 // Enable animations now. This ensures we don't animate on initial render.
316332 this . _subscriptAnimationState = 'enter' ;
317333 }
@@ -462,12 +478,6 @@ export class MatFormField implements AfterViewInit, OnDestroy, AfterContentCheck
462478 . subscribe ( ( ) => this . _needsOutlineLabelOffsetUpdateOnStable = true ) ;
463479 }
464480
465- _rerenderOutlineNotch ( ) {
466- if ( this . _floatingLabel && this . _hasOutline ( ) ) {
467- this . _foundation . notchOutline ( this . _shouldLabelFloat ( ) ) ;
468- }
469- }
470-
471481 /** Whether the floating label should always float or not. */
472482 _shouldAlwaysFloat ( ) {
473483 return this . floatLabel === 'always' ;
@@ -486,7 +496,7 @@ export class MatFormField implements AfterViewInit, OnDestroy, AfterContentCheck
486496 * the label is part of the infix, the label cannot overflow the prefix content.
487497 */
488498 _forceDisplayInfixLabel ( ) {
489- return ! this . _platform . isBrowser && this . _prefixContainer && ! this . _shouldLabelFloat ( ) ;
499+ return ! this . _platform . isBrowser && this . _prefixChildren . length && ! this . _shouldLabelFloat ( ) ;
490500 }
491501
492502 _hasFloatingLabel ( ) {
@@ -509,6 +519,17 @@ export class MatFormField implements AfterViewInit, OnDestroy, AfterContentCheck
509519 this . _control . errorState ) ? 'error' : 'hint' ;
510520 }
511521
522+ /** Refreshes the width of the outline-notch, if present. */
523+ _refreshOutlineNotchWidth ( ) {
524+ if ( ! this . _hasOutline ( ) || ! this . _floatingLabel ) {
525+ return ;
526+ }
527+ // The outline notch should be based on the label width, but needs to respect the scaling
528+ // applied to the label if it actively floats. Since the label always floats when the notch
529+ // is open, the MDC text-field floating label scaling is respected in notch width calculation.
530+ this . _outlineNotchWidth = this . _floatingLabel . getWidth ( ) * mdcTextFieldNumbers . LABEL_SCALE ;
531+ }
532+
512533 /** Does any extra processing that is required when handling the hints. */
513534 private _processHints ( ) {
514535 this . _validateHints ( ) ;
0 commit comments