mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
fix(autocomplete): throw better error when autocomplete doesn't have a panel (#4851)
Adds a more informative error if the user tries to open an autocomplete that doesn't exist or hasn't been defined yet by the `@ViewChild` query. Fixes #4844.
This commit is contained in:
committed by
Andrew Seguin
parent
9d4a34accd
commit
70b31f177a
@@ -51,6 +51,15 @@ export const MD_AUTOCOMPLETE_VALUE_ACCESSOR: any = {
|
||||
multi: true
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates an error to be thrown when attempting to use an autocomplete trigger without a panel.
|
||||
*/
|
||||
export function getMdAutocompleteMissingPanelError(): Error {
|
||||
return new Error('Attempting to open an undefined instance of `md-autocomplete`. ' +
|
||||
'Make sure that the id passed to the `mdAutocomplete` is correct and that ' +
|
||||
'you\'re attempting to open it after the ngAfterContentInit hook.');
|
||||
}
|
||||
|
||||
@Directive({
|
||||
selector: 'input[mdAutocomplete], input[matAutocomplete]',
|
||||
host: {
|
||||
@@ -124,6 +133,10 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
|
||||
|
||||
/** Opens the autocomplete suggestion panel. */
|
||||
openPanel(): void {
|
||||
if (!this.autocomplete) {
|
||||
throw getMdAutocompleteMissingPanelError();
|
||||
}
|
||||
|
||||
if (!this._overlayRef) {
|
||||
this._createOverlay();
|
||||
} else {
|
||||
@@ -177,7 +190,7 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
|
||||
|
||||
/** The currently active option, coerced to MdOption type. */
|
||||
get activeOption(): MdOption {
|
||||
if (this.autocomplete._keyManager) {
|
||||
if (this.autocomplete && this.autocomplete._keyManager) {
|
||||
return this.autocomplete._keyManager.activeItem as MdOption;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,15 +10,19 @@ import {
|
||||
} from '@angular/core';
|
||||
import {By} from '@angular/platform-browser';
|
||||
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
|
||||
import {MdAutocompleteModule, MdAutocompleteTrigger} from './index';
|
||||
import {FormControl, FormsModule, ReactiveFormsModule} from '@angular/forms';
|
||||
import {
|
||||
MdAutocompleteModule,
|
||||
MdAutocompleteTrigger,
|
||||
MdAutocomplete,
|
||||
getMdAutocompleteMissingPanelError,
|
||||
} from './index';
|
||||
import {OverlayContainer} from '../core/overlay/overlay-container';
|
||||
import {MdInputModule} from '../input/index';
|
||||
import {Dir, LayoutDirection} from '../core/rtl/dir';
|
||||
import {FormControl, FormsModule, ReactiveFormsModule} from '@angular/forms';
|
||||
import {Subscription} from 'rxjs/Subscription';
|
||||
import {ENTER, DOWN_ARROW, SPACE, UP_ARROW, ESCAPE} from '../core/keyboard/keycodes';
|
||||
import {MdOption} from '../core/option/option';
|
||||
import {MdAutocomplete} from './autocomplete';
|
||||
import {MdInputContainer} from '../input/input-container';
|
||||
import {Observable} from 'rxjs/Observable';
|
||||
import {Subject} from 'rxjs/Subject';
|
||||
@@ -26,9 +30,9 @@ import {dispatchFakeEvent} from '../core/testing/dispatch-events';
|
||||
import {createKeyboardEvent} from '../core/testing/event-objects';
|
||||
import {typeInElement} from '../core/testing/type-in-element';
|
||||
import {ScrollDispatcher} from '../core/overlay/scroll/scroll-dispatcher';
|
||||
|
||||
import 'rxjs/add/operator/map';
|
||||
|
||||
|
||||
describe('MdAutocomplete', () => {
|
||||
let overlayContainerElement: HTMLElement;
|
||||
let dir: LayoutDirection;
|
||||
@@ -50,7 +54,8 @@ describe('MdAutocomplete', () => {
|
||||
NgIfAutocomplete,
|
||||
AutocompleteWithNgModel,
|
||||
AutocompleteWithOnPushDelay,
|
||||
AutocompleteWithNativeInput
|
||||
AutocompleteWithNativeInput,
|
||||
AutocompleteWithoutPanel
|
||||
],
|
||||
providers: [
|
||||
{provide: OverlayContainer, useFactory: () => {
|
||||
@@ -1156,6 +1161,15 @@ describe('MdAutocomplete', () => {
|
||||
expect(fixture.componentInstance.mdOptions.length).toBe(2);
|
||||
}));
|
||||
|
||||
it('should throw if the user attempts to open the panel too early', async(() => {
|
||||
const fixture = TestBed.createComponent(AutocompleteWithoutPanel);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(() => {
|
||||
fixture.componentInstance.trigger.openPanel();
|
||||
}).toThrow(getMdAutocompleteMissingPanelError());
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
it('should have correct width when opened', () => {
|
||||
@@ -1422,3 +1436,11 @@ class AutocompleteWithNativeInput {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Component({
|
||||
template: `<input placeholder="Choose" [mdAutocomplete]="auto">`
|
||||
})
|
||||
class AutocompleteWithoutPanel {
|
||||
@ViewChild(MdAutocompleteTrigger) trigger: MdAutocompleteTrigger;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user