Skip to content

Commit 82c5ff0

Browse files
crisbetojelbourn
authored andcommitted
feat(autocomplete): add opened/closed events to panel (#9904)
Adds the `opened` and `closed` event emitters to the autocomplete panel, allowing consumers to react to the panel being opened and closed. Fixes #9894.
1 parent c9bd2ba commit 82c5ff0

File tree

3 files changed

+52
-4
lines changed

3 files changed

+52
-4
lines changed

src/lib/autocomplete/autocomplete-trigger.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
181181

182182
if (this._panelOpen) {
183183
this.autocomplete._isOpen = this._panelOpen = false;
184+
this.autocomplete.closed.emit();
184185

185186
if (this._overlayRef && this._overlayRef.hasAttached()) {
186187
this._overlayRef.detach();
@@ -499,8 +500,16 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
499500
this._closingActionsSubscription = this._subscribeToClosingActions();
500501
}
501502

503+
const wasOpen = this.panelOpen;
504+
502505
this.autocomplete._setVisibility();
503506
this.autocomplete._isOpen = this._panelOpen = true;
507+
508+
// We need to do an extra `panelOpen` check in here, because the
509+
// autocomplete won't be shown if there are no options.
510+
if (this.panelOpen && wasOpen !== this.panelOpen) {
511+
this.autocomplete.opened.emit();
512+
}
504513
}
505514

506515
private _getOverlayConfig(): OverlayConfig {

src/lib/autocomplete/autocomplete.spec.ts

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,38 @@ describe('MatAutocomplete', () => {
410410
expect(fixture.componentInstance.panel.isOpen).toBeTruthy(
411411
`Expected the panel to be opened on focus.`);
412412
}));
413+
414+
it('should emit an event when the panel is opened', () => {
415+
fixture.componentInstance.trigger.openPanel();
416+
fixture.detectChanges();
417+
418+
expect(fixture.componentInstance.openedSpy).toHaveBeenCalled();
419+
});
420+
421+
it('should not emit the opened event multiple times while typing', fakeAsync(() => {
422+
fixture.componentInstance.trigger.openPanel();
423+
fixture.detectChanges();
424+
425+
expect(fixture.componentInstance.openedSpy).toHaveBeenCalledTimes(1);
426+
427+
typeInElement('Alabam', input);
428+
fixture.detectChanges();
429+
tick();
430+
fixture.detectChanges();
431+
432+
expect(fixture.componentInstance.openedSpy).toHaveBeenCalledTimes(1);
433+
}));
434+
435+
it('should emit an event when the panel is closed', () => {
436+
fixture.componentInstance.trigger.openPanel();
437+
fixture.detectChanges();
438+
439+
fixture.componentInstance.trigger.closePanel();
440+
fixture.detectChanges();
441+
442+
expect(fixture.componentInstance.closedSpy).toHaveBeenCalled();
443+
});
444+
413445
});
414446

415447
it('should have the correct text direction in RTL', () => {
@@ -423,7 +455,6 @@ describe('MatAutocomplete', () => {
423455

424456
const overlayPane = overlayContainerElement.querySelector('.cdk-overlay-pane')!;
425457
expect(overlayPane.getAttribute('dir')).toEqual('rtl');
426-
427458
});
428459

429460
describe('forms integration', () => {
@@ -1814,7 +1845,7 @@ describe('MatAutocomplete', () => {
18141845
</mat-form-field>
18151846
18161847
<mat-autocomplete class="class-one class-two" #auto="matAutocomplete" [displayWith]="displayFn"
1817-
[disableRipple]="disableRipple">
1848+
[disableRipple]="disableRipple" (opened)="openedSpy()" (closed)="closedSpy()">
18181849
<mat-option *ngFor="let state of filteredStates" [value]="state">
18191850
<span> {{ state.code }}: {{ state.name }} </span>
18201851
</mat-option>
@@ -1828,6 +1859,8 @@ class SimpleAutocomplete implements OnDestroy {
18281859
floatLabel = 'auto';
18291860
width: number;
18301861
disableRipple = false;
1862+
openedSpy = jasmine.createSpy('autocomplete opened spy');
1863+
closedSpy = jasmine.createSpy('autocomplete closed spy');
18311864

18321865
@ViewChild(MatAutocompleteTrigger) trigger: MatAutocompleteTrigger;
18331866
@ViewChild(MatAutocomplete) panel: MatAutocomplete;

src/lib/autocomplete/autocomplete.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,12 @@ export class MatAutocomplete extends _MatAutocompleteMixinBase implements AfterC
126126
@Output() readonly optionSelected: EventEmitter<MatAutocompleteSelectedEvent> =
127127
new EventEmitter<MatAutocompleteSelectedEvent>();
128128

129+
/** Event that is emitted when the autocomplete panel is opened. */
130+
@Output() readonly opened: EventEmitter<void> = new EventEmitter<void>();
131+
132+
/** Event that is emitted when the autocomplete panel is closed. */
133+
@Output() readonly closed: EventEmitter<void> = new EventEmitter<void>();
134+
129135
/**
130136
* Takes classes set on the host mat-autocomplete element and applies them to the panel
131137
* inside the overlay container to allow for easy styling.
@@ -159,7 +165,7 @@ export class MatAutocomplete extends _MatAutocompleteMixinBase implements AfterC
159165

160166
ngAfterContentInit() {
161167
this._keyManager = new ActiveDescendantKeyManager<MatOption>(this.options).withWrap();
162-
// Set the initial visibiity state.
168+
// Set the initial visibility state.
163169
this._setVisibility();
164170
}
165171

@@ -184,7 +190,7 @@ export class MatAutocomplete extends _MatAutocompleteMixinBase implements AfterC
184190
this._classList['mat-autocomplete-visible'] = this.showPanel;
185191
this._classList['mat-autocomplete-hidden'] = !this.showPanel;
186192
this._changeDetectorRef.markForCheck();
187-
}
193+
}
188194

189195
/** Emits the `select` event. */
190196
_emitSelectEvent(option: MatOption): void {

0 commit comments

Comments
 (0)