From 4ea52e102fd63ee34d295815e2ea93587aaf4ec5 Mon Sep 17 00:00:00 2001 From: crisbeto Date: Wed, 28 Jun 2017 23:34:33 +0200 Subject: [PATCH] fix(select): throwing additional errors if ngModel fails to initialize If the user has an `md-select` with an `ngModel` that doesn't have a name inside a form, the forms module will throw an error, however Material will also start throwing errors, which may cause confusion. These changes add a null check so our errors don't get mixed up with the forms error. Fixes #5402. --- src/lib/select/select.html | 4 ++-- src/lib/select/select.spec.ts | 22 +++++++++++++++++++++- src/lib/select/select.ts | 7 ++++++- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/lib/select/select.html b/src/lib/select/select.html index 0eb861f9c22f..62dd450c473c 100644 --- a/src/lib/select/select.html +++ b/src/lib/select/select.html @@ -1,11 +1,11 @@
{{ placeholder }} - + {{ triggerValue }} diff --git a/src/lib/select/select.spec.ts b/src/lib/select/select.spec.ts index 55a43089a3bd..865721612681 100644 --- a/src/lib/select/select.spec.ts +++ b/src/lib/select/select.spec.ts @@ -63,7 +63,8 @@ describe('MdSelect', () => { BasicSelectWithTheming, ResetValuesSelect, FalsyValueSelect, - SelectWithGroups + SelectWithGroups, + InvalidSelectInForm ], providers: [ {provide: OverlayContainer, useFactory: () => { @@ -1942,6 +1943,17 @@ describe('MdSelect', () => { }).not.toThrow(); })); + it('should not throw selection model-related errors in addition to the errors from ngModel', + async(() => { + const fixture = TestBed.createComponent(InvalidSelectInForm); + + // The first change detection run will throw the "ngModel is missing a name" error. + expect(() => fixture.detectChanges()).toThrowError(/the name attribute must be set/g); + + // The second run shouldn't throw selection-model related errors. + expect(() => fixture.detectChanges()).not.toThrow(); + })); + }); describe('change event', () => { @@ -2842,3 +2854,11 @@ class SelectWithGroups { @ViewChild(MdSelect) select: MdSelect; @ViewChildren(MdOption) options: QueryList; } + + +@Component({ + template: `
` +}) +class InvalidSelectInForm { + value: any; +} diff --git a/src/lib/select/select.ts b/src/lib/select/select.ts index a0d3f03d70a3..2270b152cc0b 100644 --- a/src/lib/select/select.ts +++ b/src/lib/select/select.ts @@ -547,6 +547,11 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On this._setScrollTop(); } + /** Whether the select has a value. */ + _hasValue(): boolean { + return this._selectionModel && this._selectionModel.hasValue(); + } + /** * Sets the scroll position of the scroll container. This must be called after * the overlay pane is attached or the scroll container element will not yet be @@ -771,7 +776,7 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On // The farthest the panel can be scrolled before it hits the bottom const maxScroll = scrollContainerHeight - panelHeight; - if (this._selectionModel.hasValue()) { + if (this._hasValue()) { let selectedOptionOffset = this._getOptionIndex(this._selectionModel.selected[0])!; selectedOptionOffset += this._getLabelCountBeforeOption(selectedOptionOffset);