fix(autocomplete): closed event emitting when panel wasn't shown due to lack of options (#10176)

Adds an extra check to ensure that the `MatAutocomplete.closed` event won't emit when the user tried to open the panel, but it wasn't shown because there were no rendered options.

Fixes #10154.
This commit is contained in:
Kristiyan Kostadinov
2018-03-06 10:33:27 -05:00
committed by Jeremy Elbourn
parent 44637f39c0
commit 67cc2c2436
2 changed files with 52 additions and 21 deletions
+29 -21
View File
@@ -166,8 +166,10 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
}
/** Whether or not the autocomplete panel is open. */
get panelOpen(): boolean { return this._panelOpen && this.autocomplete.showPanel; }
private _panelOpen: boolean = false;
get panelOpen(): boolean {
return this._overlayAttached && this.autocomplete.showPanel;
}
private _overlayAttached: boolean = false;
/** Opens the autocomplete suggestion panel. */
openPanel(): void {
@@ -179,24 +181,30 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
closePanel(): void {
this._resetLabel();
if (this._panelOpen) {
this.autocomplete._isOpen = this._panelOpen = false;
if (!this._overlayAttached) {
return;
}
if (this.panelOpen) {
// Only emit if the panel was visible.
this.autocomplete.closed.emit();
}
if (this._overlayRef && this._overlayRef.hasAttached()) {
this._overlayRef.detach();
this._closingActionsSubscription.unsubscribe();
}
this.autocomplete._isOpen = this._overlayAttached = false;
// 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.
if (!this._componentDestroyed) {
// We need to trigger change detection manually, because
// `fromEvent` doesn't seem to do it at the proper time.
// This ensures that the label is reset when the
// user clicks outside.
this._changeDetectorRef.detectChanges();
}
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.
if (!this._componentDestroyed) {
// We need to trigger change detection manually, because
// `fromEvent` doesn't seem to do it at the proper time.
// This ensures that the label is reset when the
// user clicks outside.
this._changeDetectorRef.detectChanges();
}
}
@@ -207,11 +215,11 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
get panelClosingActions(): Observable<MatOptionSelectionChange> {
return merge(
this.optionSelections,
this.autocomplete._keyManager.tabOut.pipe(filter(() => this._panelOpen)),
this.autocomplete._keyManager.tabOut.pipe(filter(() => this._overlayAttached)),
this._closeKeyEventStream,
this._outsideClickStream,
this._overlayRef ?
this._overlayRef.detachments().pipe(filter(() => this._panelOpen)) :
this._overlayRef.detachments().pipe(filter(() => this._overlayAttached)) :
observableOf()
);
}
@@ -253,7 +261,7 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
const formField = this._formField ?
this._formField._elementRef.nativeElement : null;
return this._panelOpen &&
return this._overlayAttached &&
clickTarget !== this._element.nativeElement &&
(!formField || !formField.contains(clickTarget)) &&
(!!this._overlayRef && !this._overlayRef.overlayElement.contains(clickTarget));
@@ -503,7 +511,7 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
const wasOpen = this.panelOpen;
this.autocomplete._setVisibility();
this.autocomplete._isOpen = this._panelOpen = true;
this.autocomplete._isOpen = this._overlayAttached = true;
// We need to do an extra `panelOpen` check in here, because the
// autocomplete won't be shown if there are no options.
+23
View File
@@ -418,6 +418,16 @@ describe('MatAutocomplete', () => {
expect(fixture.componentInstance.openedSpy).toHaveBeenCalled();
});
it('should not emit the `opened` event when no options are being shown', () => {
fixture.componentInstance.filteredStates = fixture.componentInstance.states = [];
fixture.detectChanges();
fixture.componentInstance.trigger.openPanel();
fixture.detectChanges();
expect(fixture.componentInstance.openedSpy).not.toHaveBeenCalled();
});
it('should not emit the opened event multiple times while typing', fakeAsync(() => {
fixture.componentInstance.trigger.openPanel();
fixture.detectChanges();
@@ -442,6 +452,19 @@ describe('MatAutocomplete', () => {
expect(fixture.componentInstance.closedSpy).toHaveBeenCalled();
});
it('should not emit the `closed` event when no options were shown', () => {
fixture.componentInstance.filteredStates = fixture.componentInstance.states = [];
fixture.detectChanges();
fixture.componentInstance.trigger.openPanel();
fixture.detectChanges();
fixture.componentInstance.trigger.closePanel();
fixture.detectChanges();
expect(fixture.componentInstance.closedSpy).not.toHaveBeenCalled();
});
});
it('should have the correct text direction in RTL', () => {