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
This commit is contained in:
Alex Rickabaugh 2018-07-10 09:57:48 -07:00 committed by Victor Berchet
parent 53a16006d6
commit f9a6a175bf
5 changed files with 59 additions and 1 deletions

View File

@ -53,6 +53,9 @@ export function getConstructorDependencies(
const importedSymbol = reflector.getImportOfIdentifier(tokenExpr); const importedSymbol = reflector.getImportOfIdentifier(tokenExpr);
if (importedSymbol !== null && importedSymbol.from === '@angular/core') { if (importedSymbol !== null && importedSymbol.from === '@angular/core') {
switch (importedSymbol.name) { switch (importedSymbol.name) {
case 'ChangeDetectorRef':
resolved = R3ResolvedDependencyType.ChangeDetectorRef;
break;
case 'ElementRef': case 'ElementRef':
resolved = R3ResolvedDependencyType.ElementRef; resolved = R3ResolvedDependencyType.ElementRef;
break; break;

View File

@ -22,8 +22,15 @@ export const Injectable = callableClassDecorator();
export const NgModule = callableClassDecorator(); export const NgModule = callableClassDecorator();
export const Pipe = callableClassDecorator(); export const Pipe = callableClassDecorator();
export const Attribute = callableParamDecorator();
export const Inject = callableParamDecorator(); export const Inject = callableParamDecorator();
export const Self = callableParamDecorator(); export const Self = callableParamDecorator();
export const SkipSelf = callableParamDecorator(); export const SkipSelf = callableParamDecorator();
export const Optional = callableParamDecorator(); export const Optional = callableParamDecorator();
export type ModuleWithProviders<T> = any; export type ModuleWithProviders<T> = any;
export class ChangeDetectorRef {}
export class ElementRef {}
export class Injector {}
export class TemplateRef {}
export class ViewContainerRef {}

View File

@ -370,4 +370,42 @@ describe('ngtsc behavioral tests', () => {
expect(dtsContents).toContain(`import * as i1 from 'router';`); expect(dtsContents).toContain(`import * as i1 from 'router';`);
expect(dtsContents).toContain('i0.ɵNgModuleDef<TestModule, [], [i1.RouterModule], []>'); expect(dtsContents).toContain('i0.ɵNgModuleDef<TestModule, [], [i1.RouterModule], []>');
}); });
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()); }`);
});
}); });

View File

@ -106,6 +106,11 @@ export enum R3ResolvedDependencyType {
* The dependency is for `ViewContainerRef`. * The dependency is for `ViewContainerRef`.
*/ */
ViewContainerRef = 5, ViewContainerRef = 5,
/**
* The dependency is for `ChangeDetectorRef`.
*/
ChangeDetectorRef = 6,
} }
/** /**
@ -182,7 +187,7 @@ function compileInjectDependency(
} }
// Build up the arguments to the injectFn call. // 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 // 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 // parameters describing how to inject the dependency must be passed to the inject function
// that's being used. // that's being used.
@ -200,6 +205,8 @@ function compileInjectDependency(
return o.importExpr(R3.injectTemplateRef).callFn([]); return o.importExpr(R3.injectTemplateRef).callFn([]);
case R3ResolvedDependencyType.ViewContainerRef: case R3ResolvedDependencyType.ViewContainerRef:
return o.importExpr(R3.injectViewContainerRef).callFn([]); return o.importExpr(R3.injectViewContainerRef).callFn([]);
case R3ResolvedDependencyType.ChangeDetectorRef:
return o.importExpr(R3.injectChangeDetectorRef).callFn([]);
default: default:
return unsupported( return unsupported(
`Unknown R3ResolvedDependencyType: ${R3ResolvedDependencyType[dep.resolved]}`); `Unknown R3ResolvedDependencyType: ${R3ResolvedDependencyType[dep.resolved]}`);

View File

@ -97,6 +97,9 @@ export class Identifiers {
static injectViewContainerRef: static injectViewContainerRef:
o.ExternalReference = {name: 'ɵinjectViewContainerRef', moduleName: CORE}; o.ExternalReference = {name: 'ɵinjectViewContainerRef', moduleName: CORE};
static injectChangeDetectorRef:
o.ExternalReference = {name: 'ɵinjectChangeDetectorRef', moduleName: CORE};
static directiveInject: o.ExternalReference = {name: 'ɵdirectiveInject', moduleName: CORE}; static directiveInject: o.ExternalReference = {name: 'ɵdirectiveInject', moduleName: CORE};
static defineComponent: o.ExternalReference = {name: 'ɵdefineComponent', moduleName: CORE}; static defineComponent: o.ExternalReference = {name: 'ɵdefineComponent', moduleName: CORE};