From 057abefe50bdc78cae7c7f92412fb921a3dcc464 Mon Sep 17 00:00:00 2001 From: Pawel Kozlowski Date: Sun, 5 Jun 2016 04:46:03 +0200 Subject: [PATCH] fix(compiler): report errors for queries without selectors (#9018) Fixes #4489 --- .../compiler/src/metadata_resolver.ts | 17 +++++++----- .../test/linker/query_integration_spec.ts | 27 ++++++++++++++++++- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/modules/@angular/compiler/src/metadata_resolver.ts b/modules/@angular/compiler/src/metadata_resolver.ts index 8ffb455a44..cc98cadaaf 100644 --- a/modules/@angular/compiler/src/metadata_resolver.ts +++ b/modules/@angular/compiler/src/metadata_resolver.ts @@ -161,8 +161,8 @@ export class CompileMetadataResolver { var queries = []; var viewQueries = []; if (isPresent(dirMeta.queries)) { - queries = this.getQueriesMetadata(dirMeta.queries, false); - viewQueries = this.getQueriesMetadata(dirMeta.queries, true); + queries = this.getQueriesMetadata(dirMeta.queries, false, directiveType); + viewQueries = this.getQueriesMetadata(dirMeta.queries, true, directiveType); } meta = cpl.CompileDirectiveMetadata.create({ selector: dirMeta.selector, @@ -314,8 +314,8 @@ export class CompileMetadataResolver { isSelf: isSelf, isSkipSelf: isSkipSelf, isOptional: isOptional, - query: isPresent(query) ? this.getQueryMetadata(query, null) : null, - viewQuery: isPresent(viewQuery) ? this.getQueryMetadata(viewQuery, null) : null, + query: isPresent(query) ? this.getQueryMetadata(query, null, typeOrFunc) : null, + viewQuery: isPresent(viewQuery) ? this.getQueryMetadata(viewQuery, null, typeOrFunc) : null, token: this.getTokenMetadata(token) }); @@ -381,21 +381,24 @@ export class CompileMetadataResolver { } getQueriesMetadata(queries: {[key: string]: QueryMetadata}, - isViewQuery: boolean): cpl.CompileQueryMetadata[] { + isViewQuery: boolean, directiveType: Type): cpl.CompileQueryMetadata[] { var compileQueries = []; StringMapWrapper.forEach(queries, (query, propertyName) => { if (query.isViewQuery === isViewQuery) { - compileQueries.push(this.getQueryMetadata(query, propertyName)); + compileQueries.push(this.getQueryMetadata(query, propertyName, directiveType)); } }); return compileQueries; } - getQueryMetadata(q: QueryMetadata, propertyName: string): cpl.CompileQueryMetadata { + getQueryMetadata(q: QueryMetadata, propertyName: string, typeOrFunc: Type | Function): cpl.CompileQueryMetadata { var selectors; if (q.isVarBindingQuery) { selectors = q.varBindings.map(varName => this.getTokenMetadata(varName)); } else { + if (!isPresent(q.selector)) { + throw new BaseException(`Can't construct a query for the property "${propertyName}" of "${stringify(typeOrFunc)}" since the query selector wasn't defined.`); + } selectors = [this.getTokenMetadata(q.selector)]; } return new cpl.CompileQueryMetadata({ diff --git a/modules/@angular/core/test/linker/query_integration_spec.ts b/modules/@angular/core/test/linker/query_integration_spec.ts index badf0f6848..23df0c048f 100644 --- a/modules/@angular/core/test/linker/query_integration_spec.ts +++ b/modules/@angular/core/test/linker/query_integration_spec.ts @@ -11,7 +11,7 @@ import { import {TestComponentBuilder} from '@angular/compiler/testing'; import {AsyncTestCompleter} from '@angular/core/testing/testing_internal'; -import {isPresent} from '../../src/facade/lang'; +import {isPresent, stringify} from '../../src/facade/lang'; import {ObservableWrapper} from '../../src/facade/async'; import { @@ -262,6 +262,17 @@ export function main() { async.done(); }); })); + + it('should throw with descriptive error when query selectors are not present', + inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { + tcb.overrideTemplate(MyCompBroken0, '') + .createAsync(MyCompBroken0) + .catch((e) => { + expect(e.message).toEqual( + `Can't construct a query for the property "errorTrigger" of "${stringify(HasNullQueryCondition)}" since the query selector wasn't defined.`); + async.done(); + }); + })); }); describe('query for TemplateRef', () => { @@ -1086,6 +1097,11 @@ class NeedsViewContainerWithRead { createView() { this.vc.createEmbeddedView(this.template); } } +@Component({selector: 'has-null-query-condition', template: '
'}) +class HasNullQueryCondition { + @ContentChildren(null) errorTrigger; +} + @Component({ selector: 'my-comp', directives: [ @@ -1128,3 +1144,12 @@ class MyComp0 { this.list = ['1d', '2d', '3d']; } } + +@Component({ + selector: 'my-comp', + directives: [HasNullQueryCondition], + template: '' +}) +@Injectable() +class MyCompBroken0 { +}