From c71daab72100ba43638fc569b8c43070de12752a Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Wed, 31 May 2023 09:01:28 +0200 Subject: [PATCH] fix(material/select): incorrect position if initialized late Fixes that in some cases the select was resolving its overlay origin too early which caused it to position itself incorrectly when it is opened. Fixes #27063. --- src/material/select/select.ts | 27 ++++++++++++----------- tools/public_api_guard/material/select.md | 5 +---- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/src/material/select/select.ts b/src/material/select/select.ts index 86600d535a6c..47856141a745 100644 --- a/src/material/select/select.ts +++ b/src/material/select/select.ts @@ -40,7 +40,6 @@ import { import {ViewportRuler} from '@angular/cdk/scrolling'; import { AfterContentInit, - AfterViewInit, Attribute, ChangeDetectionStrategy, ChangeDetectorRef, @@ -1283,7 +1282,7 @@ export class MatSelectTrigger {} {provide: MAT_OPTION_PARENT_COMPONENT, useExisting: MatSelect}, ], }) -export class MatSelect extends _MatSelectBase implements OnInit, AfterViewInit { +export class MatSelect extends _MatSelectBase implements OnInit { @ContentChildren(MatOption, {descendants: true}) options: QueryList; @ContentChildren(MAT_OPTGROUP, {descendants: true}) optionGroups: QueryList; @ContentChild(MAT_SELECT_TRIGGER) customTrigger: MatSelectTrigger; @@ -1332,23 +1331,23 @@ export class MatSelect extends _MatSelectBase implements OnInit .pipe(takeUntil(this._destroy)) .subscribe(() => { if (this.panelOpen) { - this._overlayWidth = this._getOverlayWidth(); + this._overlayWidth = this._getOverlayWidth(this._preferredOverlayOrigin); this._changeDetectorRef.detectChanges(); } }); } - ngAfterViewInit() { - // Note that it's important that we read this in `ngAfterViewInit`, because - // reading it earlier will cause the form field to return a different element. + override open() { + // It's important that we read this as late as possible, because doing so earlier will + // return a different element since it's based on queries in the form field which may + // not have run yet. Also this needs to be assigned before we measure the overlay width. if (this._parentFormField) { this._preferredOverlayOrigin = this._parentFormField.getConnectedOverlayOrigin(); } - } - override open() { - this._overlayWidth = this._getOverlayWidth(); + this._overlayWidth = this._getOverlayWidth(this._preferredOverlayOrigin); super.open(); + // Required for the MDC form field to pick up when the overlay has been opened. this.stateChanges.next(); } @@ -1393,12 +1392,14 @@ export class MatSelect extends _MatSelectBase implements OnInit } /** Gets how wide the overlay panel should be. */ - private _getOverlayWidth(): string | number { + private _getOverlayWidth( + preferredOrigin: ElementRef | CdkOverlayOrigin | undefined, + ): string | number { if (this.panelWidth === 'auto') { const refToMeasure = - this._preferredOverlayOrigin instanceof CdkOverlayOrigin - ? this._preferredOverlayOrigin.elementRef - : this._preferredOverlayOrigin || this._elementRef; + preferredOrigin instanceof CdkOverlayOrigin + ? preferredOrigin.elementRef + : preferredOrigin || this._elementRef; return refToMeasure.nativeElement.getBoundingClientRect().width; } diff --git a/tools/public_api_guard/material/select.md b/tools/public_api_guard/material/select.md index a9eb62796514..8d5837c95218 100644 --- a/tools/public_api_guard/material/select.md +++ b/tools/public_api_guard/material/select.md @@ -7,7 +7,6 @@ import { _AbstractConstructor } from '@angular/material/core'; import { ActiveDescendantKeyManager } from '@angular/cdk/a11y'; import { AfterContentInit } from '@angular/core'; -import { AfterViewInit } from '@angular/core'; import { AnimationTriggerMetadata } from '@angular/animations'; import { BooleanInput } from '@angular/cdk/coercion'; import { CanDisable } from '@angular/material/core'; @@ -76,7 +75,7 @@ export function MAT_SELECT_SCROLL_STRATEGY_PROVIDER_FACTORY(overlay: Overlay): ( export const MAT_SELECT_TRIGGER: InjectionToken; // @public (undocumented) -export class MatSelect extends _MatSelectBase implements OnInit, AfterViewInit { +export class MatSelect extends _MatSelectBase implements OnInit { // (undocumented) close(): void; // (undocumented) @@ -86,8 +85,6 @@ export class MatSelect extends _MatSelectBase implements OnInit get hideSingleSelectionIndicator(): boolean; set hideSingleSelectionIndicator(value: BooleanInput); // (undocumented) - ngAfterViewInit(): void; - // (undocumented) ngOnInit(): void; // (undocumented) open(): void;