diff --git a/src/lib/autocomplete/autocomplete-trigger.ts b/src/lib/autocomplete/autocomplete-trigger.ts index 2667b9b16fb..2283fc670ef 100644 --- a/src/lib/autocomplete/autocomplete-trigger.ts +++ b/src/lib/autocomplete/autocomplete-trigger.ts @@ -569,13 +569,16 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy { throw getMatAutocompleteMissingPanelError(); } - if (!this._overlayRef) { + let overlayRef = this._overlayRef; + + if (!overlayRef) { this._portal = new TemplatePortal(this.autocomplete.template, this._viewContainerRef); - this._overlayRef = this._overlay.create(this._getOverlayConfig()); + overlayRef = this._overlay.create(this._getOverlayConfig()); + this._overlayRef = overlayRef; // Use the `keydownEvents` in order to take advantage of // the overlay event targeting provided by the CDK overlay. - this._overlayRef.keydownEvents().subscribe(event => { + overlayRef.keydownEvents().subscribe(event => { // Close when pressing ESCAPE or ALT + UP_ARROW, based on the a11y guidelines. // See: https://www.w3.org/TR/wai-aria-practices-1.1/#textbox-keyboard-interaction if (event.keyCode === ESCAPE || (event.keyCode === UP_ARROW && event.altKey)) { @@ -586,18 +589,21 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy { if (this._viewportRuler) { this._viewportSubscription = this._viewportRuler.change().subscribe(() => { - if (this.panelOpen && this._overlayRef) { - this._overlayRef.updateSize({width: this._getPanelWidth()}); + if (this.panelOpen && overlayRef) { + overlayRef.updateSize({width: this._getPanelWidth()}); } }); } } else { - // Update the panel width and direction, in case anything has changed. - this._overlayRef.updateSize({width: this._getPanelWidth()}); + const position = overlayRef.getConfig().positionStrategy as FlexibleConnectedPositionStrategy; + + // Update the trigger, panel width and direction, in case anything has changed. + position.setOrigin(this._getConnectedElement()); + overlayRef.updateSize({width: this._getPanelWidth()}); } - if (this._overlayRef && !this._overlayRef.hasAttached()) { - this._overlayRef.attach(this._portal); + if (overlayRef && !overlayRef.hasAttached()) { + overlayRef.attach(this._portal); this._closingActionsSubscription = this._subscribeToClosingActions(); } diff --git a/src/lib/autocomplete/autocomplete.spec.ts b/src/lib/autocomplete/autocomplete.spec.ts index d7f2c71f549..63a1c3ac912 100644 --- a/src/lib/autocomplete/autocomplete.spec.ts +++ b/src/lib/autocomplete/autocomplete.spec.ts @@ -47,6 +47,7 @@ import { MatAutocompleteModule, MatAutocompleteSelectedEvent, MatAutocompleteTrigger, + MatAutocompleteOrigin, } from './index'; @@ -2218,6 +2219,34 @@ describe('MatAutocomplete', () => { const fixture = createComponent(AutocompleteWithDifferentOrigin); fixture.detectChanges(); + fixture.componentInstance.connectedTo = fixture.componentInstance.alternateOrigin; + fixture.detectChanges(); + fixture.componentInstance.trigger.openPanel(); + fixture.detectChanges(); + zone.simulateZoneExit(); + + const overlayRect = + overlayContainerElement.querySelector('.cdk-overlay-pane')!.getBoundingClientRect(); + const originRect = fixture.nativeElement.querySelector('.origin').getBoundingClientRect(); + + expect(Math.floor(overlayRect.top)).toBe(Math.floor(originRect.bottom), + 'Expected autocomplete panel to align with the bottom of the new origin.'); + }); + + it('should be able to change the origin after the panel has been opened', () => { + const fixture = createComponent(AutocompleteWithDifferentOrigin); + + fixture.detectChanges(); + fixture.componentInstance.trigger.openPanel(); + fixture.detectChanges(); + zone.simulateZoneExit(); + + fixture.componentInstance.trigger.closePanel(); + fixture.detectChanges(); + + fixture.componentInstance.connectedTo = fixture.componentInstance.alternateOrigin; + fixture.detectChanges(); + fixture.componentInstance.trigger.openPanel(); fixture.detectChanges(); zone.simulateZoneExit(); @@ -2603,7 +2632,7 @@ class AutocompleteWithNumberInputAndNgModel { @@ -2623,8 +2652,10 @@ class AutocompleteWithNumberInputAndNgModel { }) class AutocompleteWithDifferentOrigin { @ViewChild(MatAutocompleteTrigger) trigger: MatAutocompleteTrigger; + @ViewChild(MatAutocompleteOrigin) alternateOrigin: MatAutocompleteOrigin; selectedValue: string; values = ['one', 'two', 'three']; + connectedTo?: MatAutocompleteOrigin; } @Component({ diff --git a/src/lib/autocomplete/public-api.ts b/src/lib/autocomplete/public-api.ts index ce1c67cdd3e..8f425eec12f 100644 --- a/src/lib/autocomplete/public-api.ts +++ b/src/lib/autocomplete/public-api.ts @@ -9,4 +9,4 @@ export * from './autocomplete'; export * from './autocomplete-module'; export * from './autocomplete-trigger'; - +export * from './autocomplete-origin';