mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
fix(autocomplete): handle optionSelections being accessed early (#8802)
Currently the `MatAutocompleteTrigger` will throw if `optionSelections` is accessed before `ngAfterViewInit`. These changes add a fallback stream that will be replaced once everything is initialized. Fixes #4616.
This commit is contained in:
committed by
Jeremy Elbourn
parent
9dc43de540
commit
4e1a6a8e18
@@ -41,6 +41,7 @@ import {MatFormField} from '@angular/material/form-field';
|
||||
import {DOCUMENT} from '@angular/common';
|
||||
import {Observable} from 'rxjs/Observable';
|
||||
import {Subject} from 'rxjs/Subject';
|
||||
import {defer} from 'rxjs/observable/defer';
|
||||
import {fromEvent} from 'rxjs/observable/fromEvent';
|
||||
import {merge} from 'rxjs/observable/merge';
|
||||
import {of as observableOf} from 'rxjs/observable/of';
|
||||
@@ -202,9 +203,17 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
|
||||
}
|
||||
|
||||
/** Stream of autocomplete option selections. */
|
||||
get optionSelections(): Observable<MatOptionSelectionChange> {
|
||||
return merge(...this.autocomplete.options.map(option => option.onSelectionChange));
|
||||
}
|
||||
optionSelections: Observable<MatOptionSelectionChange> = defer(() => {
|
||||
if (this.autocomplete && this.autocomplete.options) {
|
||||
return merge(...this.autocomplete.options.map(option => option.onSelectionChange));
|
||||
}
|
||||
|
||||
// If there are any subscribers before `ngAfterViewInit`, the `autocomplete` will be undefined.
|
||||
// Return a stream that we'll replace with the real one once everything is in place.
|
||||
return this._zone.onStable
|
||||
.asObservable()
|
||||
.pipe(take(1), switchMap(() => this.optionSelections));
|
||||
});
|
||||
|
||||
/** The currently active option, coerced to MatOption type. */
|
||||
get activeOption(): MatOption | null {
|
||||
|
||||
@@ -31,7 +31,7 @@ import {
|
||||
flush,
|
||||
} from '@angular/core/testing';
|
||||
import {FormControl, FormsModule, ReactiveFormsModule} from '@angular/forms';
|
||||
import {MatOption} from '@angular/material/core';
|
||||
import {MatOption, MatOptionSelectionChange} from '@angular/material/core';
|
||||
import {MatFormField, MatFormFieldModule} from '@angular/material/form-field';
|
||||
import {By} from '@angular/platform-browser';
|
||||
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
|
||||
@@ -1309,6 +1309,45 @@ describe('MatAutocomplete', () => {
|
||||
expect(componentOptions[0].deselect).toHaveBeenCalled();
|
||||
componentOptions.slice(1).forEach(option => expect(option.deselect).not.toHaveBeenCalled());
|
||||
}));
|
||||
|
||||
it('should emit an event when an option is selected', fakeAsync(() => {
|
||||
const spy = jasmine.createSpy('option selection spy');
|
||||
const subscription = fixture.componentInstance.trigger.optionSelections.subscribe(spy);
|
||||
const option = overlayContainerElement.querySelector('mat-option') as HTMLElement;
|
||||
option.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(spy).toHaveBeenCalledWith(jasmine.any(MatOptionSelectionChange));
|
||||
subscription.unsubscribe();
|
||||
}));
|
||||
|
||||
it('should handle `optionSelections` being accessed too early', fakeAsync(() => {
|
||||
overlayContainer.ngOnDestroy();
|
||||
fixture.destroy();
|
||||
fixture = TestBed.createComponent(SimpleAutocomplete);
|
||||
|
||||
let spy = jasmine.createSpy('option selection spy');
|
||||
let subscription: Subscription;
|
||||
|
||||
expect(fixture.componentInstance.trigger.autocomplete).toBeFalsy();
|
||||
expect(() => {
|
||||
subscription = fixture.componentInstance.trigger.optionSelections.subscribe(spy);
|
||||
}).not.toThrow();
|
||||
|
||||
fixture.detectChanges();
|
||||
fixture.componentInstance.trigger.openPanel();
|
||||
fixture.detectChanges();
|
||||
zone.simulateZoneExit();
|
||||
|
||||
const option = overlayContainerElement.querySelector('mat-option') as HTMLElement;
|
||||
|
||||
option.click();
|
||||
fixture.detectChanges();
|
||||
zone.simulateZoneExit();
|
||||
|
||||
expect(spy).toHaveBeenCalledWith(jasmine.any(MatOptionSelectionChange));
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
describe('panel closing', () => {
|
||||
|
||||
Reference in New Issue
Block a user