From b2df2a2a5fe46c39a22d3e1da342d07fef5113a3 Mon Sep 17 00:00:00 2001 From: mmalerba Date: Fri, 16 Dec 2016 15:56:01 -0800 Subject: [PATCH] chore(input): improve error when md-input-container has no md-input (#2253) * fix cryptic error when md-input-container has no md-input * fix typo --- src/lib/input/input-container-errors.ts | 8 +++++ src/lib/input/input-container.spec.ts | 46 ++++++++++++++++++++----- src/lib/input/input-container.ts | 7 +++- 3 files changed, 51 insertions(+), 10 deletions(-) diff --git a/src/lib/input/input-container-errors.ts b/src/lib/input/input-container-errors.ts index 214997f60f6..55485ac1b14 100644 --- a/src/lib/input/input-container-errors.ts +++ b/src/lib/input/input-container-errors.ts @@ -20,3 +20,11 @@ export class MdInputContainerDuplicatedHintError extends MdError { super(`A hint was already declared for 'align="${align}"'.`); } } + + +export class MdInputContainerMissingMdInputError extends MdError { + constructor() { + super('md-input-container must contain an md-input directive. Did you forget to add md-input ' + + 'to the native input or textarea element?'); + } +} diff --git a/src/lib/input/input-container.spec.ts b/src/lib/input/input-container.spec.ts index c0c2bd1985c..defb792f5a9 100644 --- a/src/lib/input/input-container.spec.ts +++ b/src/lib/input/input-container.spec.ts @@ -6,6 +6,11 @@ import {MdInputModule} from './input'; import {MdInputContainer} from './input-container'; import {MdPlatform} from '../core/platform/platform'; import {PlatformModule} from '../core/platform/index'; +import { + MdInputContainerMissingMdInputError, + MdInputContainerPlaceholderConflictError, + MdInputContainerDuplicatedHintError +} from './input-container-errors'; describe('MdInputContainer', function () { @@ -35,6 +40,7 @@ describe('MdInputContainer', function () { MdInputContainerNumberTestController, MdTextareaWithBindings, MdInputContainerWithDisabled, + MdInputContainerMissingMdInputTestController ], }); @@ -150,25 +156,29 @@ describe('MdInputContainer', function () { it('validates there\'s only one hint label per side', () => { let fixture = TestBed.createComponent(MdInputContainerInvalidHintTestController); - expect(() => fixture.detectChanges()).toThrow(); - // TODO(jelbourn): .toThrow(new MdInputContainerDuplicatedHintError('start')); - // See https://github.com/angular/angular/issues/8348 + expect(() => fixture.detectChanges()).toThrowError( + angularWrappedErrorMessage(new MdInputContainerDuplicatedHintError('start'))); }); it('validates there\'s only one hint label per side (attribute)', () => { let fixture = TestBed.createComponent(MdInputContainerInvalidHint2TestController); - expect(() => fixture.detectChanges()).toThrow(); - // TODO(jelbourn): .toThrow(new MdInputContainerDuplicatedHintError('start')); - // See https://github.com/angular/angular/issues/8348 + expect(() => fixture.detectChanges()).toThrowError( + angularWrappedErrorMessage(new MdInputContainerDuplicatedHintError('start'))); }); it('validates there\'s only one placeholder', () => { let fixture = TestBed.createComponent(MdInputContainerInvalidPlaceholderTestController); - expect(() => fixture.detectChanges()).toThrow(); - // TODO(jelbourn): .toThrow(new MdInputContainerPlaceholderConflictError()); - // See https://github.com/angular/angular/issues/8348 + expect(() => fixture.detectChanges()).toThrowError( + angularWrappedErrorMessage(new MdInputContainerPlaceholderConflictError())); + }); + + it('validates that md-input child is present', () => { + let fixture = TestBed.createComponent(MdInputContainerMissingMdInputTestController); + + expect(() => fixture.detectChanges()).toThrowError( + angularWrappedErrorMessage(new MdInputContainerMissingMdInputError())); }); it('validates the type', () => { @@ -406,3 +416,21 @@ class MdTextareaWithBindings { cols: number = 8; wrap: string = 'hard'; } + +@Component({ + template: `` +}) +class MdInputContainerMissingMdInputTestController {} + +/** + * Gets a RegExp used to detect an angular wrapped error message. + * See https://github.com/angular/angular/issues/8348 + */ +const angularWrappedErrorMessage = (e: Error) => + new RegExp(`.*caused by: ${regexpEscape(e.message)}$`); + +/** + * Escape a string for use inside a RegExp. + * Based on https://github.com/sindresorhus/escape-string-regex + */ +const regexpEscape = (s: string) => s.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&'); diff --git a/src/lib/input/input-container.ts b/src/lib/input/input-container.ts index 4fedf96e7cd..241a6738633 100644 --- a/src/lib/input/input-container.ts +++ b/src/lib/input/input-container.ts @@ -19,7 +19,8 @@ import {getSupportedInputTypes} from '../core/platform/features'; import { MdInputContainerUnsupportedTypeError, MdInputContainerPlaceholderConflictError, - MdInputContainerDuplicatedHintError + MdInputContainerDuplicatedHintError, + MdInputContainerMissingMdInputError } from './input-container-errors'; @@ -218,6 +219,10 @@ export class MdInputContainer implements AfterContentInit { @ContentChildren(MdHint) _hintChildren: QueryList; ngAfterContentInit() { + if (!this._mdInputChild) { + throw new MdInputContainerMissingMdInputError(); + } + this._validateHints(); this._validatePlaceholders();