@@ -27,6 +27,7 @@ import {
27
27
ViewChild ,
28
28
ViewEncapsulation ,
29
29
ANIMATION_MODULE_TYPE ,
30
+ afterNextRender ,
30
31
} from '@angular/core' ;
31
32
import { AbstractControlDirective } from '@angular/forms' ;
32
33
import { ThemePalette } from '@angular/material/core' ;
@@ -245,7 +246,7 @@ export class MatFormField
245
246
// If the appearance has been switched to `outline`, the label offset needs to be updated.
246
247
// The update can happen once the view has been re-checked, but not immediately because
247
248
// the view has not been updated and the notched-outline floating label is not present.
248
- this . _needsOutlineLabelOffsetUpdateOnStable = true ;
249
+ this . _updateOutlineLabelOffsetAfterNextRender ( ) ;
249
250
}
250
251
}
251
252
private _appearance : MatFormFieldAppearance = DEFAULT_APPEARANCE ;
@@ -300,12 +301,15 @@ export class MatFormField
300
301
private _destroyed = new Subject < void > ( ) ;
301
302
private _isFocused : boolean | null = null ;
302
303
private _explicitFormFieldControl : MatFormFieldControl < any > ;
303
- private _needsOutlineLabelOffsetUpdateOnStable = false ;
304
304
305
305
constructor (
306
306
public _elementRef : ElementRef ,
307
307
private _changeDetectorRef : ChangeDetectorRef ,
308
- private _ngZone : NgZone ,
308
+ /**
309
+ * @deprecated not needed, to be removed.
310
+ * @breaking -change 19.0.0 remove this param
311
+ */
312
+ private _unusedNgZone : NgZone ,
309
313
private _dir : Directionality ,
310
314
private _platform : Platform ,
311
315
@Optional ( )
@@ -485,30 +489,13 @@ export class MatFormField
485
489
* The floating label in the docked state needs to account for prefixes. The horizontal offset
486
490
* is calculated whenever the appearance changes to `outline`, the prefixes change, or when the
487
491
* form field is added to the DOM. This method sets up all subscriptions which are needed to
488
- * trigger the label offset update. In general, we want to avoid performing measurements often,
489
- * so we rely on the `NgZone` as indicator when the offset should be recalculated, instead of
490
- * checking every change detection cycle.
492
+ * trigger the label offset update.
491
493
*/
492
494
private _initializeOutlineLabelOffsetSubscriptions ( ) {
493
- // Whenever the prefix changes, schedule an update of the label offset.
494
- this . _prefixChildren . changes . subscribe (
495
- ( ) => ( this . _needsOutlineLabelOffsetUpdateOnStable = true ) ,
496
- ) ;
497
-
498
- // Note that we have to run outside of the `NgZone` explicitly, in order to avoid
499
- // throwing users into an infinite loop if `zone-patch-rxjs` is included.
500
- this . _ngZone . runOutsideAngular ( ( ) => {
501
- this . _ngZone . onStable . pipe ( takeUntil ( this . _destroyed ) ) . subscribe ( ( ) => {
502
- if ( this . _needsOutlineLabelOffsetUpdateOnStable ) {
503
- this . _needsOutlineLabelOffsetUpdateOnStable = false ;
504
- this . _updateOutlineLabelOffset ( ) ;
505
- }
506
- } ) ;
507
- } ) ;
508
-
495
+ this . _prefixChildren . changes . subscribe ( ( ) => this . _updateOutlineLabelOffsetAfterNextRender ( ) ) ;
509
496
this . _dir . change
510
497
. pipe ( takeUntil ( this . _destroyed ) )
511
- . subscribe ( ( ) => ( this . _needsOutlineLabelOffsetUpdateOnStable = true ) ) ;
498
+ . subscribe ( ( ) => this . _updateOutlineLabelOffsetAfterNextRender ( ) ) ;
512
499
}
513
500
514
501
/** Whether the floating label should always float or not. */
@@ -652,41 +639,43 @@ export class MatFormField
652
639
* not need to do this because they use a fixed width for prefixes. Hence, they can simply
653
640
* incorporate the horizontal offset into their default text-field styles.
654
641
*/
655
- private _updateOutlineLabelOffset ( ) {
656
- if ( ! this . _platform . isBrowser || ! this . _hasOutline ( ) || ! this . _floatingLabel ) {
657
- return ;
658
- }
659
- const floatingLabel = this . _floatingLabel . element ;
660
- // If no prefix is displayed, reset the outline label offset from potential
661
- // previous label offset updates.
662
- if ( ! ( this . _iconPrefixContainer || this . _textPrefixContainer ) ) {
663
- floatingLabel . style . transform = '' ;
664
- return ;
665
- }
666
- // If the form field is not attached to the DOM yet (e.g. in a tab), we defer
667
- // the label offset update until the zone stabilizes.
668
- if ( ! this . _isAttachedToDom ( ) ) {
669
- this . _needsOutlineLabelOffsetUpdateOnStable = true ;
670
- return ;
671
- }
672
- const iconPrefixContainer = this . _iconPrefixContainer ?. nativeElement ;
673
- const textPrefixContainer = this . _textPrefixContainer ?. nativeElement ;
674
- const iconPrefixContainerWidth = iconPrefixContainer ?. getBoundingClientRect ( ) . width ?? 0 ;
675
- const textPrefixContainerWidth = textPrefixContainer ?. getBoundingClientRect ( ) . width ?? 0 ;
676
- // If the directionality is RTL, the x-axis transform needs to be inverted. This
677
- // is because `transformX` does not change based on the page directionality.
678
- const negate = this . _dir . value === 'rtl' ? '-1' : '1' ;
679
- const prefixWidth = `${ iconPrefixContainerWidth + textPrefixContainerWidth } px` ;
680
- const labelOffset = `var(--mat-mdc-form-field-label-offset-x, 0px)` ;
681
- const labelHorizontalOffset = `calc(${ negate } * (${ prefixWidth } + ${ labelOffset } ))` ;
682
-
683
- // Update the translateX of the floating label to account for the prefix container,
684
- // but allow the CSS to override this setting via a CSS variable when the label is
685
- // floating.
686
- floatingLabel . style . transform = `var(
687
- --mat-mdc-form-field-label-transform,
688
- ${ FLOATING_LABEL_DEFAULT_DOCKED_TRANSFORM } translateX(${ labelHorizontalOffset } )
689
- )` ;
642
+ private _updateOutlineLabelOffsetAfterNextRender ( ) {
643
+ afterNextRender ( ( ) => {
644
+ if ( ! this . _platform . isBrowser || ! this . _hasOutline ( ) || ! this . _floatingLabel ) {
645
+ return ;
646
+ }
647
+ const floatingLabel = this . _floatingLabel . element ;
648
+ // If no prefix is displayed, reset the outline label offset from potential
649
+ // previous label offset updates.
650
+ if ( ! ( this . _iconPrefixContainer || this . _textPrefixContainer ) ) {
651
+ floatingLabel . style . transform = '' ;
652
+ return ;
653
+ }
654
+ // If the form field is not attached to the DOM yet (e.g. in a tab), we defer
655
+ // the label offset update until after the next render.
656
+ if ( ! this . _isAttachedToDom ( ) ) {
657
+ this . _updateOutlineLabelOffsetAfterNextRender ( ) ;
658
+ return ;
659
+ }
660
+ const iconPrefixContainer = this . _iconPrefixContainer ?. nativeElement ;
661
+ const textPrefixContainer = this . _textPrefixContainer ?. nativeElement ;
662
+ const iconPrefixContainerWidth = iconPrefixContainer ?. getBoundingClientRect ( ) . width ?? 0 ;
663
+ const textPrefixContainerWidth = textPrefixContainer ?. getBoundingClientRect ( ) . width ?? 0 ;
664
+ // If the directionality is RTL, the x-axis transform needs to be inverted. This
665
+ // is because `transformX` does not change based on the page directionality.
666
+ const negate = this . _dir . value === 'rtl' ? '-1' : '1' ;
667
+ const prefixWidth = `${ iconPrefixContainerWidth + textPrefixContainerWidth } px` ;
668
+ const labelOffset = `var(--mat-mdc-form-field-label-offset-x, 0px)` ;
669
+ const labelHorizontalOffset = `calc(${ negate } * (${ prefixWidth } + ${ labelOffset } ))` ;
670
+
671
+ // Update the translateX of the floating label to account for the prefix container,
672
+ // but allow the CSS to override this setting via a CSS variable when the label is
673
+ // floating.
674
+ floatingLabel . style . transform = `var(
675
+ --mat-mdc-form-field-label-transform,
676
+ ${ FLOATING_LABEL_DEFAULT_DOCKED_TRANSFORM } translateX(${ labelHorizontalOffset } )
677
+ )` ;
678
+ } ) ;
690
679
}
691
680
692
681
/** Checks whether the form field is attached to the DOM. */
0 commit comments