Skip to content

Commit

Permalink
fix(material/autocomplete): optionSelections not emitting when the li…
Browse files Browse the repository at this point in the history
…st 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)
  • Loading branch information
crisbeto authored and andrewseguin committed Jan 13, 2022
1 parent e01e579 commit eae436f
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 5 deletions.
28 changes: 28 additions & 0 deletions src/material-experimental/mdc-autocomplete/autocomplete.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2279,6 +2279,34 @@ describe('MDC-based MatAutocomplete', () => {
subscription!.unsubscribe();
}));

it('should emit to `optionSelections` if the list of options changes', fakeAsync(() => {
const spy = jasmine.createSpy('option selection spy');
const subscription = fixture.componentInstance.trigger.optionSelections.subscribe(spy);
const openAndSelectFirstOption = () => {
fixture.detectChanges();
fixture.componentInstance.trigger.openPanel();
fixture.detectChanges();
zone.simulateZoneExit();
(overlayContainerElement.querySelector('mat-option') as HTMLElement).click();
fixture.detectChanges();
zone.simulateZoneExit();
};

fixture.componentInstance.states = [{code: 'OR', name: 'Oregon'}];
fixture.detectChanges();

openAndSelectFirstOption();
expect(spy).toHaveBeenCalledTimes(1);

fixture.componentInstance.states = [{code: 'WV', name: 'West Virginia'}];
fixture.detectChanges();

openAndSelectFirstOption();
expect(spy).toHaveBeenCalledTimes(2);

subscription!.unsubscribe();
}));

it('should reposition the panel when the amount of options changes', fakeAsync(() => {
let formField = fixture.debugElement.query(By.css('.mat-mdc-form-field'))!.nativeElement;
let inputReference = formField.querySelector('.mdc-text-field');
Expand Down
15 changes: 10 additions & 5 deletions src/material/autocomplete/autocomplete-trigger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ import {
} from '@angular/material/core';
import {MAT_FORM_FIELD, MatFormField} from '@angular/material/form-field';
import {defer, fromEvent, merge, Observable, of as observableOf, Subject, Subscription} from 'rxjs';
import {delay, filter, map, switchMap, take, tap} from 'rxjs/operators';
import {delay, filter, map, switchMap, take, tap, startWith} from 'rxjs/operators';

import {
_MatAutocompleteBase,
Expand Down Expand Up @@ -309,10 +309,15 @@ export abstract class _MatAutocompleteTriggerBase
);
}

/** Stream of autocomplete option selections. */
/** Stream of changes to the selection state of the autocomplete options. */
readonly optionSelections: Observable<MatOptionSelectionChange> = defer(() => {
if (this.autocomplete && this.autocomplete.options) {
return merge(...this.autocomplete.options.map(option => option.onSelectionChange));
const options = this.autocomplete ? this.autocomplete.options : null;

if (options) {
return options.changes.pipe(
startWith(options),
switchMap(() => merge(...options.map(option => option.onSelectionChange)))
);
}

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

// Implemented as part of ControlValueAccessor.
writeValue(value: any): void {
Promise.resolve(null).then(() => this._setTriggerValue(value));
Promise.resolve().then(() => this._setTriggerValue(value));
}

// Implemented as part of ControlValueAccessor.
Expand Down
28 changes: 28 additions & 0 deletions src/material/autocomplete/autocomplete.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2265,6 +2265,34 @@ describe('MatAutocomplete', () => {
subscription!.unsubscribe();
}));

it('should emit to `optionSelections` if the list of options changes', fakeAsync(() => {
const spy = jasmine.createSpy('option selection spy');
const subscription = fixture.componentInstance.trigger.optionSelections.subscribe(spy);
const openAndSelectFirstOption = () => {
fixture.detectChanges();
fixture.componentInstance.trigger.openPanel();
fixture.detectChanges();
zone.simulateZoneExit();
(overlayContainerElement.querySelector('mat-option') as HTMLElement).click();
fixture.detectChanges();
zone.simulateZoneExit();
};

fixture.componentInstance.states = [{code: 'OR', name: 'Oregon'}];
fixture.detectChanges();

openAndSelectFirstOption();
expect(spy).toHaveBeenCalledTimes(1);

fixture.componentInstance.states = [{code: 'WV', name: 'West Virginia'}];
fixture.detectChanges();

openAndSelectFirstOption();
expect(spy).toHaveBeenCalledTimes(2);

subscription!.unsubscribe();
}));

it('should reposition the panel when the amount of options changes', fakeAsync(() => {
const formField = fixture.debugElement.query(By.css('.mat-form-field'))!.nativeElement;
const inputReference = formField.querySelector('.mat-form-field-flex');
Expand Down

0 comments on commit eae436f

Please sign in to comment.