From f9a6a175bfec6a8eb769f550ee361521ba8ffd92 Mon Sep 17 00:00:00 2001 From: Alex Rickabaugh Date: Tue, 10 Jul 2018 09:57:48 -0700 Subject: [PATCH] fix(ivy): properly inject all special token types (#24862) Previously ngtsc had a few bugs handling special token types: * Injector was not properly translated to INJECTOR * ChangeDetectorRef was not injected via injectChangeDetectorRef() This commit fixes these two bugs, and also adds a test to ensure they continue to work correctly. PR Close #24862 --- .../src/ngtsc/annotations/src/util.ts | 3 ++ .../test/ngtsc/fake_core/index.ts | 7 ++++ .../compiler-cli/test/ngtsc/ngtsc_spec.ts | 38 +++++++++++++++++++ packages/compiler/src/render3/r3_factory.ts | 9 ++++- .../compiler/src/render3/r3_identifiers.ts | 3 ++ 5 files changed, 59 insertions(+), 1 deletion(-) diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/util.ts b/packages/compiler-cli/src/ngtsc/annotations/src/util.ts index 6322ddef86..fe82cfdfca 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/util.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/util.ts @@ -53,6 +53,9 @@ export function getConstructorDependencies( const importedSymbol = reflector.getImportOfIdentifier(tokenExpr); if (importedSymbol !== null && importedSymbol.from === '@angular/core') { switch (importedSymbol.name) { + case 'ChangeDetectorRef': + resolved = R3ResolvedDependencyType.ChangeDetectorRef; + break; case 'ElementRef': resolved = R3ResolvedDependencyType.ElementRef; break; diff --git a/packages/compiler-cli/test/ngtsc/fake_core/index.ts b/packages/compiler-cli/test/ngtsc/fake_core/index.ts index 7999ab1cc4..0735b5621a 100644 --- a/packages/compiler-cli/test/ngtsc/fake_core/index.ts +++ b/packages/compiler-cli/test/ngtsc/fake_core/index.ts @@ -22,8 +22,15 @@ export const Injectable = callableClassDecorator(); export const NgModule = callableClassDecorator(); export const Pipe = callableClassDecorator(); +export const Attribute = callableParamDecorator(); export const Inject = callableParamDecorator(); export const Self = callableParamDecorator(); export const SkipSelf = callableParamDecorator(); export const Optional = callableParamDecorator(); export type ModuleWithProviders = any; + +export class ChangeDetectorRef {} +export class ElementRef {} +export class Injector {} +export class TemplateRef {} +export class ViewContainerRef {} \ No newline at end of file diff --git a/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts b/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts index 7ad4cf2ffa..0d5fc832b5 100644 --- a/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts +++ b/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts @@ -370,4 +370,42 @@ describe('ngtsc behavioral tests', () => { expect(dtsContents).toContain(`import * as i1 from 'router';`); expect(dtsContents).toContain('i0.ɵNgModuleDef'); }); + + it('should inject special types according to the metadata', () => { + writeConfig(); + write(`test.ts`, ` + import { + Attribute, + ChangeDetectorRef, + Component, + ElementRef, + Injector, + TemplateRef, + ViewContainerRef, + } from '@angular/core'; + + @Component({ + selector: 'test', + template: 'Test', + }) + class FooCmp { + constructor( + @Attribute("test") attr: string, + cdr: ChangeDetectorRef, + er: ElementRef, + i: Injector, + tr: TemplateRef, + vcr: ViewContainerRef, + ) {} + } + `); + + const exitCode = main(['-p', basePath], errorSpy); + expect(errorSpy).not.toHaveBeenCalled(); + expect(exitCode).toBe(0); + const jsContents = getContents('test.js'); + expect(jsContents) + .toContain( + `factory: function FooCmp_Factory() { return new FooCmp(i0.ɵinjectAttribute("test"), i0.ɵinjectChangeDetectorRef(), i0.ɵinjectElementRef(), i0.ɵdirectiveInject(i0.INJECTOR), i0.ɵinjectTemplateRef(), i0.ɵinjectViewContainerRef()); }`); + }); }); diff --git a/packages/compiler/src/render3/r3_factory.ts b/packages/compiler/src/render3/r3_factory.ts index 178434026d..ba50ae1550 100644 --- a/packages/compiler/src/render3/r3_factory.ts +++ b/packages/compiler/src/render3/r3_factory.ts @@ -106,6 +106,11 @@ export enum R3ResolvedDependencyType { * The dependency is for `ViewContainerRef`. */ ViewContainerRef = 5, + + /** + * The dependency is for `ChangeDetectorRef`. + */ + ChangeDetectorRef = 6, } /** @@ -182,7 +187,7 @@ function compileInjectDependency( } // Build up the arguments to the injectFn call. - const injectArgs = [dep.token]; + const injectArgs = [token]; // If this dependency is optional or otherwise has non-default flags, then additional // parameters describing how to inject the dependency must be passed to the inject function // that's being used. @@ -200,6 +205,8 @@ function compileInjectDependency( return o.importExpr(R3.injectTemplateRef).callFn([]); case R3ResolvedDependencyType.ViewContainerRef: return o.importExpr(R3.injectViewContainerRef).callFn([]); + case R3ResolvedDependencyType.ChangeDetectorRef: + return o.importExpr(R3.injectChangeDetectorRef).callFn([]); default: return unsupported( `Unknown R3ResolvedDependencyType: ${R3ResolvedDependencyType[dep.resolved]}`); diff --git a/packages/compiler/src/render3/r3_identifiers.ts b/packages/compiler/src/render3/r3_identifiers.ts index 698326b6a8..72f2f30ba0 100644 --- a/packages/compiler/src/render3/r3_identifiers.ts +++ b/packages/compiler/src/render3/r3_identifiers.ts @@ -97,6 +97,9 @@ export class Identifiers { static injectViewContainerRef: o.ExternalReference = {name: 'ɵinjectViewContainerRef', moduleName: CORE}; + static injectChangeDetectorRef: + o.ExternalReference = {name: 'ɵinjectChangeDetectorRef', moduleName: CORE}; + static directiveInject: o.ExternalReference = {name: 'ɵdirectiveInject', moduleName: CORE}; static defineComponent: o.ExternalReference = {name: 'ɵdefineComponent', moduleName: CORE};