diff --git a/src/lib/autocomplete/autocomplete-trigger.ts b/src/lib/autocomplete/autocomplete-trigger.ts index fe7ef1fb7215..ea86763a5722 100644 --- a/src/lib/autocomplete/autocomplete-trigger.ts +++ b/src/lib/autocomplete/autocomplete-trigger.ts @@ -15,6 +15,7 @@ import { PositionStrategy, ScrollStrategy, } from '@angular/cdk/overlay'; +import {TemplatePortal} from '@angular/cdk/portal'; import {DOCUMENT} from '@angular/common'; import {filter, take, switchMap, delay, tap, map} from 'rxjs/operators'; import { @@ -29,6 +30,7 @@ import { NgZone, OnDestroy, Optional, + ViewContainerRef, } from '@angular/core'; import {ViewportRuler} from '@angular/cdk/scrolling'; import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms'; @@ -115,9 +117,9 @@ export function getMatAutocompleteMissingPanelError(): Error { }) export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy { private _overlayRef: OverlayRef | null; + private _portal: TemplatePortal; private _componentDestroyed = false; private _autocompleteDisabled = false; - private _autocomplete: MatAutocomplete; private _scrollStrategy: () => ScrollStrategy; /** Old value of the native input. Used to work around issues with the `input` event on IE. */ @@ -130,7 +132,7 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy { private _manuallyFloatingLabel = false; /** The subscription for closing actions (some are bound to document). */ - private _closingActionsSubscription = Subscription.EMPTY; + private _closingActionsSubscription: Subscription; /** Subscription to viewport size changes. */ private _viewportSubscription = Subscription.EMPTY; @@ -164,12 +166,7 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy { _onTouched = () => {}; /** The autocomplete panel to be attached to this trigger. */ - @Input('matAutocomplete') - get autocomplete(): MatAutocomplete { return this._autocomplete; } - set autocomplete(value: MatAutocomplete) { - this._autocomplete = value; - this._detachOverlay(); - } + @Input('matAutocomplete') autocomplete: MatAutocomplete; /** * Reference relative to which to position the autocomplete panel. @@ -193,8 +190,8 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy { this._autocompleteDisabled = coerceBooleanProperty(value); } - constructor(private _element: ElementRef, - private _overlay: Overlay, + constructor(private _element: ElementRef, private _overlay: Overlay, + private _viewContainerRef: ViewContainerRef, private _zone: NgZone, private _changeDetectorRef: ChangeDetectorRef, @Inject(MAT_AUTOCOMPLETE_SCROLL_STRATEGY) scrollStrategy: any, @@ -249,9 +246,12 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy { this.autocomplete.closed.emit(); } - this.autocomplete._isOpen = false; - this._detachOverlay(); + this.autocomplete._isOpen = this._overlayAttached = false; + if (this._overlayRef && this._overlayRef.hasAttached()) { + this._overlayRef.detach(); + this._closingActionsSubscription.unsubscribe(); + } // Note that in some cases this can end up being called after the component is destroyed. // Add a check to ensure that we don't try to run change detection on a destroyed view. @@ -570,6 +570,7 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy { } if (!this._overlayRef) { + this._portal = new TemplatePortal(this.autocomplete.template, this._viewContainerRef); this._overlayRef = this._overlay.create(this._getOverlayConfig()); // Use the `keydownEvents` in order to take advantage of @@ -596,7 +597,7 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy { } if (this._overlayRef && !this._overlayRef.hasAttached()) { - this._overlayRef.attach(this.autocomplete._portal); + this._overlayRef.attach(this._portal); this._closingActionsSubscription = this._subscribeToClosingActions(); } @@ -612,14 +613,6 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy { } } - private _detachOverlay() { - this._overlayAttached = false; - this._closingActionsSubscription.unsubscribe(); - if (this._overlayRef) { - this._overlayRef.detach(); - } - } - private _getOverlayConfig(): OverlayConfig { return new OverlayConfig({ positionStrategy: this._getOverlayPosition(), diff --git a/src/lib/autocomplete/autocomplete.spec.ts b/src/lib/autocomplete/autocomplete.spec.ts index 1b2c205c860c..055cf6da1e5d 100644 --- a/src/lib/autocomplete/autocomplete.spec.ts +++ b/src/lib/autocomplete/autocomplete.spec.ts @@ -2233,35 +2233,6 @@ describe('MatAutocomplete', () => { expect(formControl.value).toBe('Cal', 'Expected new value to be propagated to model'); })); - it('should work when dynamically changing the autocomplete', () => { - const fixture = createComponent(DynamicallyChangingAutocomplete); - fixture.detectChanges(); - const input = fixture.debugElement.query(By.css('input')).nativeElement; - - dispatchFakeEvent(input, 'focusin'); - fixture.detectChanges(); - - expect(overlayContainerElement.textContent).toContain('First', - `Expected panel to display the option of the first autocomplete.`); - expect(overlayContainerElement.textContent).not.toContain('Second', - `Expected panel to not display the option of the second autocomplete.`); - - dispatchFakeEvent(document, 'click'); - fixture.detectChanges(); - - fixture.componentInstance.trigger.autocomplete = fixture.componentInstance.autoTow; - fixture.detectChanges(); - - dispatchFakeEvent(input, 'focusin'); - fixture.detectChanges(); - - expect(overlayContainerElement.textContent).not.toContain('First', - `Expected panel to not display the option of the first autocomplete.`); - expect(overlayContainerElement.textContent).toContain('Second', - `Expected panel to display the option of the second autocomplete.`); - - }); - }); @Component({ @@ -2648,21 +2619,3 @@ class AutocompleteWithNativeAutocompleteAttribute { }) class InputWithoutAutocompleteAndDisabled { } - -@Component({ - template: ` - - - First - - - - Second - - `, -}) -class DynamicallyChangingAutocomplete { - @ViewChild('autoOne') autoOne: MatAutocomplete; - @ViewChild('autoTow') autoTow: MatAutocomplete; - @ViewChild(MatAutocompleteTrigger) trigger: MatAutocompleteTrigger; -} diff --git a/src/lib/autocomplete/autocomplete.ts b/src/lib/autocomplete/autocomplete.ts index 8c31c34e4c47..bbd5a0ca1b26 100644 --- a/src/lib/autocomplete/autocomplete.ts +++ b/src/lib/autocomplete/autocomplete.ts @@ -24,8 +24,6 @@ import { TemplateRef, ViewChild, ViewEncapsulation, - AfterViewInit, - ViewContainerRef, } from '@angular/core'; import { CanDisableRipple, @@ -35,7 +33,6 @@ import { MatOption, mixinDisableRipple, } from '@angular/material/core'; -import {TemplatePortal} from '@angular/cdk/portal'; /** @@ -95,7 +92,7 @@ export function MAT_AUTOCOMPLETE_DEFAULT_OPTIONS_FACTORY(): MatAutocompleteDefau ] }) export class MatAutocomplete extends _MatAutocompleteMixinBase implements AfterContentInit, - AfterViewInit, CanDisableRipple { + CanDisableRipple { /** Manages active item in option list based on key events. */ _keyManager: ActiveDescendantKeyManager; @@ -103,9 +100,6 @@ export class MatAutocomplete extends _MatAutocompleteMixinBase implements AfterC /** Whether the autocomplete panel should be visible, depending on option length. */ showPanel: boolean = false; - /** @docs-private */ - _portal: TemplatePortal; - /** Whether the autocomplete panel is open. */ get isOpen(): boolean { return this._isOpen && this.showPanel; } _isOpen: boolean = false; @@ -171,17 +165,12 @@ export class MatAutocomplete extends _MatAutocompleteMixinBase implements AfterC constructor( private _changeDetectorRef: ChangeDetectorRef, private _elementRef: ElementRef, - private _viewContainerRef: ViewContainerRef, @Inject(MAT_AUTOCOMPLETE_DEFAULT_OPTIONS) defaults: MatAutocompleteDefaultOptions) { super(); this._autoActiveFirstOption = !!defaults.autoActiveFirstOption; } - ngAfterViewInit() { - this._portal = new TemplatePortal(this.template, this._viewContainerRef); - } - ngAfterContentInit() { this._keyManager = new ActiveDescendantKeyManager(this.options).withWrap(); // Set the initial visibility state.