Skip to content

Commit eae436f

Browse files
crisbetoandrewseguin
authored andcommitted
fix(material/autocomplete): optionSelections not emitting when the list of options changes (#14813)
Fixes the `MatAutocompleteTrigger.optionSelections` stopping to emit when the list options has been swapped out. Fixes #14777. (cherry picked from commit 3fd6f0e)
1 parent e01e579 commit eae436f

File tree

3 files changed

+66
-5
lines changed

3 files changed

+66
-5
lines changed

src/material-experimental/mdc-autocomplete/autocomplete.spec.ts

+28
Original file line numberDiff line numberDiff line change
@@ -2279,6 +2279,34 @@ describe('MDC-based MatAutocomplete', () => {
22792279
subscription!.unsubscribe();
22802280
}));
22812281

2282+
it('should emit to `optionSelections` if the list of options changes', fakeAsync(() => {
2283+
const spy = jasmine.createSpy('option selection spy');
2284+
const subscription = fixture.componentInstance.trigger.optionSelections.subscribe(spy);
2285+
const openAndSelectFirstOption = () => {
2286+
fixture.detectChanges();
2287+
fixture.componentInstance.trigger.openPanel();
2288+
fixture.detectChanges();
2289+
zone.simulateZoneExit();
2290+
(overlayContainerElement.querySelector('mat-option') as HTMLElement).click();
2291+
fixture.detectChanges();
2292+
zone.simulateZoneExit();
2293+
};
2294+
2295+
fixture.componentInstance.states = [{code: 'OR', name: 'Oregon'}];
2296+
fixture.detectChanges();
2297+
2298+
openAndSelectFirstOption();
2299+
expect(spy).toHaveBeenCalledTimes(1);
2300+
2301+
fixture.componentInstance.states = [{code: 'WV', name: 'West Virginia'}];
2302+
fixture.detectChanges();
2303+
2304+
openAndSelectFirstOption();
2305+
expect(spy).toHaveBeenCalledTimes(2);
2306+
2307+
subscription!.unsubscribe();
2308+
}));
2309+
22822310
it('should reposition the panel when the amount of options changes', fakeAsync(() => {
22832311
let formField = fixture.debugElement.query(By.css('.mat-mdc-form-field'))!.nativeElement;
22842312
let inputReference = formField.querySelector('.mdc-text-field');

src/material/autocomplete/autocomplete-trigger.ts

+10-5
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 {
5353
_MatAutocompleteBase,
@@ -309,10 +309,15 @@ export abstract class _MatAutocompleteTriggerBase
309309
);
310310
}
311311

312-
/** Stream of autocomplete option selections. */
312+
/** Stream of changes to the selection state of the autocomplete options. */
313313
readonly optionSelections: Observable<MatOptionSelectionChange> = defer(() => {
314-
if (this.autocomplete && this.autocomplete.options) {
315-
return merge(...this.autocomplete.options.map(option => option.onSelectionChange));
314+
const options = this.autocomplete ? this.autocomplete.options : null;
315+
316+
if (options) {
317+
return options.changes.pipe(
318+
startWith(options),
319+
switchMap(() => merge(...options.map(option => option.onSelectionChange)))
320+
);
316321
}
317322

318323
// If there are any subscribers before `ngAfterViewInit`, the `autocomplete` will be undefined.
@@ -360,7 +365,7 @@ export abstract class _MatAutocompleteTriggerBase
360365

361366
// Implemented as part of ControlValueAccessor.
362367
writeValue(value: any): void {
363-
Promise.resolve(null).then(() => this._setTriggerValue(value));
368+
Promise.resolve().then(() => this._setTriggerValue(value));
364369
}
365370

366371
// Implemented as part of ControlValueAccessor.

src/material/autocomplete/autocomplete.spec.ts

+28
Original file line numberDiff line numberDiff line change
@@ -2265,6 +2265,34 @@ describe('MatAutocomplete', () => {
22652265
subscription!.unsubscribe();
22662266
}));
22672267

2268+
it('should emit to `optionSelections` if the list of options changes', fakeAsync(() => {
2269+
const spy = jasmine.createSpy('option selection spy');
2270+
const subscription = fixture.componentInstance.trigger.optionSelections.subscribe(spy);
2271+
const openAndSelectFirstOption = () => {
2272+
fixture.detectChanges();
2273+
fixture.componentInstance.trigger.openPanel();
2274+
fixture.detectChanges();
2275+
zone.simulateZoneExit();
2276+
(overlayContainerElement.querySelector('mat-option') as HTMLElement).click();
2277+
fixture.detectChanges();
2278+
zone.simulateZoneExit();
2279+
};
2280+
2281+
fixture.componentInstance.states = [{code: 'OR', name: 'Oregon'}];
2282+
fixture.detectChanges();
2283+
2284+
openAndSelectFirstOption();
2285+
expect(spy).toHaveBeenCalledTimes(1);
2286+
2287+
fixture.componentInstance.states = [{code: 'WV', name: 'West Virginia'}];
2288+
fixture.detectChanges();
2289+
2290+
openAndSelectFirstOption();
2291+
expect(spy).toHaveBeenCalledTimes(2);
2292+
2293+
subscription!.unsubscribe();
2294+
}));
2295+
22682296
it('should reposition the panel when the amount of options changes', fakeAsync(() => {
22692297
const formField = fixture.debugElement.query(By.css('.mat-form-field'))!.nativeElement;
22702298
const inputReference = formField.querySelector('.mat-form-field-flex');

0 commit comments

Comments
 (0)