diff --git a/modules/@angular/compiler/src/metadata_resolver.ts b/modules/@angular/compiler/src/metadata_resolver.ts index b8366f3239..1d0e06ff5f 100644 --- a/modules/@angular/compiler/src/metadata_resolver.ts +++ b/modules/@angular/compiler/src/metadata_resolver.ts @@ -112,14 +112,16 @@ export class CompileMetadataResolver { }); changeDetectionStrategy = cmpMeta.changeDetection; if (isPresent(dirMeta.viewProviders)) { - viewProviders = this.getProvidersMetadata(dirMeta.viewProviders); + viewProviders = this.getProvidersMetadata( + verifyNonBlankProviders(directiveType, dirMeta.viewProviders, 'viewProviders')); } moduleUrl = componentModuleUrl(this._reflector, directiveType, cmpMeta); } var providers: any[] /** TODO #9100 */ = []; if (isPresent(dirMeta.providers)) { - providers = this.getProvidersMetadata(dirMeta.providers); + providers = this.getProvidersMetadata( + verifyNonBlankProviders(directiveType, dirMeta.providers, 'providers')); } var queries: any[] /** TODO #9100 */ = []; var viewQueries: any[] /** TODO #9100 */ = []; @@ -423,6 +425,23 @@ function flattenArray(tree: any[], out: Array): void { } } +function verifyNonBlankProviders( + directiveType: Type, providersTree: any[], providersType: string): any[] { + var flat: any[] = []; + var errMsg: string; + + flattenArray(providersTree, flat); + for (var i = 0; i < flat.length; i++) { + if (isBlank(flat[i])) { + errMsg = flat.map(provider => isBlank(provider) ? '?' : stringify(provider)).join(', '); + throw new BaseException( + `One or more of ${providersType} for "${stringify(directiveType)}" were not defined: [${errMsg}].`); + } + } + + return providersTree; +} + function isStaticType(value: any): boolean { return isStringMap(value) && isPresent(value['name']) && isPresent(value['filePath']); } diff --git a/modules/@angular/compiler/test/metadata_resolver_spec.ts b/modules/@angular/compiler/test/metadata_resolver_spec.ts index 9d338be9ab..837535fe89 100644 --- a/modules/@angular/compiler/test/metadata_resolver_spec.ts +++ b/modules/@angular/compiler/test/metadata_resolver_spec.ts @@ -64,6 +64,20 @@ export function main() { .toThrowError(`Can't resolve all parameters for NonAnnotatedService: (?).`); })); + it('should throw with descriptive error message when one of providers is not present', + inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { + expect(() => resolver.getDirectiveMetadata(MyBrokenComp3)) + .toThrowError( + `One or more of providers for "MyBrokenComp3" were not defined: [?, SimpleService, ?].`); + })); + + it('should throw with descriptive error message when one of viewProviders is not present', + inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { + expect(() => resolver.getDirectiveMetadata(MyBrokenComp4)) + .toThrowError( + `One or more of viewProviders for "MyBrokenComp4" were not defined: [?, SimpleService, ?].`); + })); + it('should throw an error when the interpolation config has invalid symbols', inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { expect(() => resolver.getDirectiveMetadata(ComponentWithInvalidInterpolation1)) @@ -162,6 +176,18 @@ class MyBrokenComp2 { constructor(dependency: NonAnnotatedService) {} } +@Injectable() +class SimpleService { +} + +@Component({selector: 'my-broken-comp', template: '', providers: [null, SimpleService, [null]]}) +class MyBrokenComp3 { +} + +@Component({selector: 'my-broken-comp', template: '', viewProviders: [null, SimpleService, [null]]}) +class MyBrokenComp4 { +} + @Component({selector: 'someSelector', template: '', interpolation: [' ', ' ']}) class ComponentWithInvalidInterpolation1 { }