Skip to content

Commit 530999a

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 ab0f30d commit 530999a

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 {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 {MatAutocomplete} from './autocomplete';
5353
import {MatAutocompleteOrigin} from './autocomplete-origin';
@@ -334,10 +334,15 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, AfterViewIn
334334
);
335335
}
336336

337-
/** Stream of autocomplete option selections. */
337+
/** Stream of changes to the selection state of the autocomplete options. */
338338
readonly optionSelections: Observable<MatOptionSelectionChange> = defer(() => {
339-
if (this.autocomplete && this.autocomplete.options) {
340-
return merge(...this.autocomplete.options.map(option => option.onSelectionChange));
339+
const options = this.autocomplete ? this.autocomplete.options : null;
340+
341+
if (options) {
342+
return options.changes.pipe(
343+
startWith(options),
344+
switchMap(() => merge(...options.map(option => option.onSelectionChange)))
345+
);
341346
}
342347

343348
// If there are any subscribers before `ngAfterViewInit`, the `autocomplete` will be undefined.
@@ -377,7 +382,7 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, AfterViewIn
377382

378383
// Implemented as part of ControlValueAccessor.
379384
writeValue(value: any): void {
380-
Promise.resolve(null).then(() => this._setTriggerValue(value));
385+
Promise.resolve().then(() => this._setTriggerValue(value));
381386
}
382387

383388
// 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
@@ -1970,6 +1970,34 @@ describe('MatAutocomplete', () => {
19701970
subscription!.unsubscribe();
19711971
}));
19721972

1973+
it('should emit to `optionSelections` if the list of options changes', fakeAsync(() => {
1974+
const spy = jasmine.createSpy('option selection spy');
1975+
const subscription = fixture.componentInstance.trigger.optionSelections.subscribe(spy);
1976+
const openAndSelectFirstOption = () => {
1977+
fixture.detectChanges();
1978+
fixture.componentInstance.trigger.openPanel();
1979+
fixture.detectChanges();
1980+
zone.simulateZoneExit();
1981+
(overlayContainerElement.querySelector('mat-option') as HTMLElement).click();
1982+
fixture.detectChanges();
1983+
zone.simulateZoneExit();
1984+
};
1985+
1986+
fixture.componentInstance.states = [{code: 'OR', name: 'Oregon'}];
1987+
fixture.detectChanges();
1988+
1989+
openAndSelectFirstOption();
1990+
expect(spy).toHaveBeenCalledTimes(1);
1991+
1992+
fixture.componentInstance.states = [{code: 'WV', name: 'West Virginia'}];
1993+
fixture.detectChanges();
1994+
1995+
openAndSelectFirstOption();
1996+
expect(spy).toHaveBeenCalledTimes(2);
1997+
1998+
subscription!.unsubscribe();
1999+
}));
2000+
19732001
it('should reposition the panel when the amount of options changes', fakeAsync(() => {
19742002
let formField = fixture.debugElement.query(By.css('.mat-form-field'))!.nativeElement;
19752003
let inputReference = formField.querySelector('.mat-form-field-flex');

0 commit comments

Comments
 (0)