Skip to content

Commit 6e3b436

Browse files
committed
fix(autocomplete): optionSelections not emitting when the list of options changes
Fixes the `MatAutocompleteTrigger.optionSelections` stopping to emit when the list options has been swapped out. Fixes #14777.
1 parent f0c7a25 commit 6e3b436

File tree

2 files changed

+38
-5
lines changed

2 files changed

+38
-5
lines changed

src/material/autocomplete/autocomplete-trigger.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ import {
4747
} from '@angular/material/core';
4848
import {MAT_FORM_FIELD, MatFormField} from '@angular/material/form-field';
4949
import {defer, fromEvent, merge, Observable, of as observableOf, Subject, Subscription} from 'rxjs';
50-
import {delay, filter, map, switchMap, take, tap} from 'rxjs/operators';
50+
import {delay, filter, map, switchMap, take, tap, startWith} from 'rxjs/operators';
5151

5252
import {_MatAutocompleteBase} from './autocomplete';
5353
import {_MatAutocompleteOriginBase} from './autocomplete-origin';
@@ -318,10 +318,15 @@ export abstract class _MatAutocompleteTriggerBase implements ControlValueAccesso
318318
);
319319
}
320320

321-
/** Stream of autocomplete option selections. */
321+
/** Stream of changes to the selection state of the autocomplete options. */
322322
readonly optionSelections: Observable<MatOptionSelectionChange> = defer(() => {
323-
if (this.autocomplete && this.autocomplete.options) {
324-
return merge(...this.autocomplete.options.map(option => option.onSelectionChange));
323+
const options = this.autocomplete ? this.autocomplete.options : null;
324+
325+
if (options) {
326+
return options.changes.pipe(
327+
startWith(options),
328+
switchMap(() => merge(...options.map(option => option.onSelectionChange)))
329+
);
325330
}
326331

327332
// If there are any subscribers before `ngAfterViewInit`, the `autocomplete` will be undefined.
@@ -362,7 +367,7 @@ export abstract class _MatAutocompleteTriggerBase implements ControlValueAccesso
362367

363368
// Implemented as part of ControlValueAccessor.
364369
writeValue(value: any): void {
365-
Promise.resolve(null).then(() => this._setTriggerValue(value));
370+
Promise.resolve().then(() => this._setTriggerValue(value));
366371
}
367372

368373
// Implemented as part of ControlValueAccessor.

src/material/autocomplete/autocomplete.spec.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1994,6 +1994,34 @@ describe('MatAutocomplete', () => {
19941994
subscription!.unsubscribe();
19951995
}));
19961996

1997+
it('should emit to `optionSelections` if the list of options changes', fakeAsync(() => {
1998+
const spy = jasmine.createSpy('option selection spy');
1999+
const subscription = fixture.componentInstance.trigger.optionSelections.subscribe(spy);
2000+
const openAndSelectFirstOption = () => {
2001+
fixture.detectChanges();
2002+
fixture.componentInstance.trigger.openPanel();
2003+
fixture.detectChanges();
2004+
zone.simulateZoneExit();
2005+
(overlayContainerElement.querySelector('mat-option') as HTMLElement).click();
2006+
fixture.detectChanges();
2007+
zone.simulateZoneExit();
2008+
};
2009+
2010+
fixture.componentInstance.states = [{code: 'OR', name: 'Oregon'}];
2011+
fixture.detectChanges();
2012+
2013+
openAndSelectFirstOption();
2014+
expect(spy).toHaveBeenCalledTimes(1);
2015+
2016+
fixture.componentInstance.states = [{code: 'WV', name: 'West Virginia'}];
2017+
fixture.detectChanges();
2018+
2019+
openAndSelectFirstOption();
2020+
expect(spy).toHaveBeenCalledTimes(2);
2021+
2022+
subscription!.unsubscribe();
2023+
}));
2024+
19972025
it('should reposition the panel when the amount of options changes', fakeAsync(() => {
19982026
let formField = fixture.debugElement.query(By.css('.mat-form-field'))!.nativeElement;
19992027
let inputReference = formField.querySelector('.mat-form-field-flex');

0 commit comments

Comments
 (0)