diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/directive.ts b/packages/compiler-cli/src/ngtsc/annotations/src/directive.ts index fdc508a398..a0caee7461 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/directive.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/directive.ts @@ -341,7 +341,8 @@ export function extractQueryMetadata( // Extract the predicate let predicate: Expression|string[]|null = null; - if (arg instanceof Reference) { + if (arg instanceof Reference || arg instanceof DynamicValue) { + // References and predicates that could not be evaluated statically are emitted as is. predicate = new WrappedNodeExpr(node); } else if (typeof arg === 'string') { predicate = [arg]; diff --git a/packages/compiler-cli/test/ngtsc/fake_core/index.ts b/packages/compiler-cli/test/ngtsc/fake_core/index.ts index d0ed030493..6c296d2137 100644 --- a/packages/compiler-cli/test/ngtsc/fake_core/index.ts +++ b/packages/compiler-cli/test/ngtsc/fake_core/index.ts @@ -57,6 +57,10 @@ export class ɵNgModuleFactory { constructor(public clazz: T) {} } +export class InjectionToken { + constructor(description: string) {} +} + export function forwardRef(fn: () => T): T { return fn(); } diff --git a/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts b/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts index 695c4cc2b9..76c840ab42 100644 --- a/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts +++ b/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts @@ -22,9 +22,9 @@ const trim = (input: string): string => input.replace(/\s+/g, ' ').trim(); const varRegExp = (name: string): RegExp => new RegExp(`var \\w+ = \\[\"${name}\"\\];`); -const viewQueryRegExp = (descend: boolean, ref?: string): RegExp => { +const viewQueryRegExp = (predicate: string, descend: boolean, ref?: string): RegExp => { const maybeRef = ref ? `, ${ref}` : ``; - return new RegExp(`i0\\.ɵɵviewQuery\\(\\w+, ${descend}${maybeRef}\\)`); + return new RegExp(`i0\\.ɵɵviewQuery\\(${predicate}, ${descend}${maybeRef}\\)`); }; const contentQueryRegExp = (predicate: string, descend: boolean, ref?: string): RegExp => { @@ -2396,7 +2396,7 @@ runInEachFileSystem(os => { // match `i0.ɵɵcontentQuery(dirIndex, _c1, true, TemplateRef)` expect(jsContents).toMatch(contentQueryRegExp('\\w+', true, 'TemplateRef')); // match `i0.ɵɵviewQuery(_c2, true, null)` - expect(jsContents).toMatch(viewQueryRegExp(true)); + expect(jsContents).toMatch(viewQueryRegExp('\\w+', true)); }); it('should generate queries for directives', () => { @@ -2430,7 +2430,7 @@ runInEachFileSystem(os => { // match `i0.ɵɵviewQuery(_c2, true)` // Note that while ViewQuery doesn't necessarily make sense on a directive, because it doesn't // have a view, we still need to handle it because a component could extend the directive. - expect(jsContents).toMatch(viewQueryRegExp(true)); + expect(jsContents).toMatch(viewQueryRegExp('\\w+', true)); }); it('should handle queries that use forwardRef', () => { @@ -2461,6 +2461,30 @@ runInEachFileSystem(os => { expect(jsContents).toMatch(contentQueryRegExp('_c0', true)); }); + it('should handle queries that use an InjectionToken', () => { + env.write(`test.ts`, ` + import {Component, ContentChild, InjectionToken, ViewChild} from '@angular/core'; + + const TOKEN = new InjectionToken('token'); + + @Component({ + selector: 'test', + template: '
', + }) + class FooCmp { + @ViewChild(TOKEN as any) viewChild: any; + @ContentChild(TOKEN as any) contentChild: any; + } + `); + + env.driveMain(); + const jsContents = env.getContents('test.js'); + // match `i0.ɵɵviewQuery(TOKEN, true, null)` + expect(jsContents).toMatch(viewQueryRegExp('TOKEN', true)); + // match `i0.ɵɵcontentQuery(dirIndex, TOKEN, true, null)` + expect(jsContents).toMatch(contentQueryRegExp('TOKEN', true)); + }); + it('should compile expressions that write keys', () => { env.write(`test.ts`, ` import {Component, ContentChild, TemplateRef, ViewContainerRef, forwardRef} from '@angular/core';