Skip to content

Commit bfb76b3

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 a9e8776 commit bfb76b3

File tree

2 files changed

+38
-5
lines changed

2 files changed

+38
-5
lines changed

src/lib/autocomplete/autocomplete-trigger.ts

+10-5
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
} from '@angular/cdk/overlay';
1818
import {TemplatePortal} from '@angular/cdk/portal';
1919
import {DOCUMENT} from '@angular/common';
20-
import {filter, take, switchMap, delay, tap, map} from 'rxjs/operators';
20+
import {filter, take, switchMap, delay, tap, map, startWith} from 'rxjs/operators';
2121
import {
2222
ChangeDetectorRef,
2323
Directive,
@@ -293,10 +293,15 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
293293
);
294294
}
295295

296-
/** Stream of autocomplete option selections. */
296+
/** Stream of changes to the selection state of the autocomplete options. */
297297
readonly optionSelections: Observable<MatOptionSelectionChange> = defer(() => {
298-
if (this.autocomplete && this.autocomplete.options) {
299-
return merge(...this.autocomplete.options.map(option => option.onSelectionChange));
298+
const options = this.autocomplete ? this.autocomplete.options : null;
299+
300+
if (options) {
301+
return options.changes.pipe(
302+
startWith(options),
303+
switchMap(() => merge(...options.map(option => option.onSelectionChange)))
304+
);
300305
}
301306

302307
// If there are any subscribers before `ngAfterViewInit`, the `autocomplete` will be undefined.
@@ -339,7 +344,7 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
339344

340345
// Implemented as part of ControlValueAccessor.
341346
writeValue(value: any): void {
342-
Promise.resolve(null).then(() => this._setTriggerValue(value));
347+
Promise.resolve().then(() => this._setTriggerValue(value));
343348
}
344349

345350
// Implemented as part of ControlValueAccessor.

src/lib/autocomplete/autocomplete.spec.ts

+28
Original file line numberDiff line numberDiff line change
@@ -1713,6 +1713,34 @@ describe('MatAutocomplete', () => {
17131713
subscription!.unsubscribe();
17141714
}));
17151715

1716+
it('should emit to `optionSelections` if the list of options changes', fakeAsync(() => {
1717+
const spy = jasmine.createSpy('option selection spy');
1718+
const subscription = fixture.componentInstance.trigger.optionSelections.subscribe(spy);
1719+
const openAndSelectFirstOption = () => {
1720+
fixture.detectChanges();
1721+
fixture.componentInstance.trigger.openPanel();
1722+
fixture.detectChanges();
1723+
zone.simulateZoneExit();
1724+
(overlayContainerElement.querySelector('mat-option') as HTMLElement).click();
1725+
fixture.detectChanges();
1726+
zone.simulateZoneExit();
1727+
};
1728+
1729+
fixture.componentInstance.states = [{code: 'OR', name: 'Oregon'}];
1730+
fixture.detectChanges();
1731+
1732+
openAndSelectFirstOption();
1733+
expect(spy).toHaveBeenCalledTimes(1);
1734+
1735+
fixture.componentInstance.states = [{code: 'WV', name: 'West Virginia'}];
1736+
fixture.detectChanges();
1737+
1738+
openAndSelectFirstOption();
1739+
expect(spy).toHaveBeenCalledTimes(2);
1740+
1741+
subscription!.unsubscribe();
1742+
}));
1743+
17161744
it('should reposition the panel when the amount of options changes', fakeAsync(() => {
17171745
let formField = fixture.debugElement.query(By.css('.mat-form-field')).nativeElement;
17181746
let inputReference = formField.querySelector('.mat-form-field-flex');

0 commit comments

Comments
 (0)