Skip to content

Commit 70b31f1

Browse files
crisbetoandrewseguin
authored andcommitted
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.
1 parent 9d4a34a commit 70b31f1

File tree

2 files changed

+41
-6
lines changed

2 files changed

+41
-6
lines changed

src/lib/autocomplete/autocomplete-trigger.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,15 @@ export const MD_AUTOCOMPLETE_VALUE_ACCESSOR: any = {
5151
multi: true
5252
};
5353

54+
/**
55+
* Creates an error to be thrown when attempting to use an autocomplete trigger without a panel.
56+
*/
57+
export function getMdAutocompleteMissingPanelError(): Error {
58+
return new Error('Attempting to open an undefined instance of `md-autocomplete`. ' +
59+
'Make sure that the id passed to the `mdAutocomplete` is correct and that ' +
60+
'you\'re attempting to open it after the ngAfterContentInit hook.');
61+
}
62+
5463
@Directive({
5564
selector: 'input[mdAutocomplete], input[matAutocomplete]',
5665
host: {
@@ -124,6 +133,10 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
124133

125134
/** Opens the autocomplete suggestion panel. */
126135
openPanel(): void {
136+
if (!this.autocomplete) {
137+
throw getMdAutocompleteMissingPanelError();
138+
}
139+
127140
if (!this._overlayRef) {
128141
this._createOverlay();
129142
} else {
@@ -177,7 +190,7 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
177190

178191
/** The currently active option, coerced to MdOption type. */
179192
get activeOption(): MdOption {
180-
if (this.autocomplete._keyManager) {
193+
if (this.autocomplete && this.autocomplete._keyManager) {
181194
return this.autocomplete._keyManager.activeItem as MdOption;
182195
}
183196
}

src/lib/autocomplete/autocomplete.spec.ts

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,29 @@ import {
1010
} from '@angular/core';
1111
import {By} from '@angular/platform-browser';
1212
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
13-
import {MdAutocompleteModule, MdAutocompleteTrigger} from './index';
13+
import {FormControl, FormsModule, ReactiveFormsModule} from '@angular/forms';
14+
import {
15+
MdAutocompleteModule,
16+
MdAutocompleteTrigger,
17+
MdAutocomplete,
18+
getMdAutocompleteMissingPanelError,
19+
} from './index';
1420
import {OverlayContainer} from '../core/overlay/overlay-container';
1521
import {MdInputModule} from '../input/index';
1622
import {Dir, LayoutDirection} from '../core/rtl/dir';
17-
import {FormControl, FormsModule, ReactiveFormsModule} from '@angular/forms';
1823
import {Subscription} from 'rxjs/Subscription';
1924
import {ENTER, DOWN_ARROW, SPACE, UP_ARROW, ESCAPE} from '../core/keyboard/keycodes';
2025
import {MdOption} from '../core/option/option';
21-
import {MdAutocomplete} from './autocomplete';
2226
import {MdInputContainer} from '../input/input-container';
2327
import {Observable} from 'rxjs/Observable';
2428
import {Subject} from 'rxjs/Subject';
2529
import {dispatchFakeEvent} from '../core/testing/dispatch-events';
2630
import {createKeyboardEvent} from '../core/testing/event-objects';
2731
import {typeInElement} from '../core/testing/type-in-element';
2832
import {ScrollDispatcher} from '../core/overlay/scroll/scroll-dispatcher';
29-
3033
import 'rxjs/add/operator/map';
3134

35+
3236
describe('MdAutocomplete', () => {
3337
let overlayContainerElement: HTMLElement;
3438
let dir: LayoutDirection;
@@ -50,7 +54,8 @@ describe('MdAutocomplete', () => {
5054
NgIfAutocomplete,
5155
AutocompleteWithNgModel,
5256
AutocompleteWithOnPushDelay,
53-
AutocompleteWithNativeInput
57+
AutocompleteWithNativeInput,
58+
AutocompleteWithoutPanel
5459
],
5560
providers: [
5661
{provide: OverlayContainer, useFactory: () => {
@@ -1156,6 +1161,15 @@ describe('MdAutocomplete', () => {
11561161
expect(fixture.componentInstance.mdOptions.length).toBe(2);
11571162
}));
11581163

1164+
it('should throw if the user attempts to open the panel too early', async(() => {
1165+
const fixture = TestBed.createComponent(AutocompleteWithoutPanel);
1166+
fixture.detectChanges();
1167+
1168+
expect(() => {
1169+
fixture.componentInstance.trigger.openPanel();
1170+
}).toThrow(getMdAutocompleteMissingPanelError());
1171+
}));
1172+
11591173
});
11601174

11611175
it('should have correct width when opened', () => {
@@ -1422,3 +1436,11 @@ class AutocompleteWithNativeInput {
14221436
});
14231437
}
14241438
}
1439+
1440+
1441+
@Component({
1442+
template: `<input placeholder="Choose" [mdAutocomplete]="auto">`
1443+
})
1444+
class AutocompleteWithoutPanel {
1445+
@ViewChild(MdAutocompleteTrigger) trigger: MdAutocompleteTrigger;
1446+
}

0 commit comments

Comments
 (0)