From 2fd4c372d5a17f17ccf3b06316eec442cd03efba Mon Sep 17 00:00:00 2001 From: Kara Erickson Date: Thu, 25 Oct 2018 13:58:33 -0700 Subject: [PATCH] fix(ivy): resolve forward refs in providers (#26766) PR Close #26766 --- packages/core/src/di/r3_injector.ts | 11 ++-- packages/core/test/render3/component_spec.ts | 58 ++++++++++++++++---- 2 files changed, 51 insertions(+), 18 deletions(-) diff --git a/packages/core/src/di/r3_injector.ts b/packages/core/src/di/r3_injector.ts index a0c17d6010..c558860a93 100644 --- a/packages/core/src/di/r3_injector.ts +++ b/packages/core/src/di/r3_injector.ts @@ -357,20 +357,19 @@ function providerToRecord(provider: SingleProvider): Record { * @param provider provider to convert to factory */ export function providerToFactory(provider: SingleProvider): () => any { - let token = resolveForwardRef(provider); let factory: (() => any)|undefined = undefined; if (isTypeProvider(provider)) { - return injectableDefFactory(provider); + return injectableDefFactory(resolveForwardRef(provider)); } else { - token = resolveForwardRef(provider.provide); if (isValueProvider(provider)) { - factory = () => provider.useValue; + factory = () => resolveForwardRef(provider.useValue); } else if (isExistingProvider(provider)) { - factory = () => inject(provider.useExisting); + factory = () => inject(resolveForwardRef(provider.useExisting)); } else if (isFactoryProvider(provider)) { factory = () => provider.useFactory(...injectArgs(provider.deps || [])); } else { - const classRef = (provider as StaticClassProvider | ClassProvider).useClass || token; + const classRef = resolveForwardRef( + (provider as StaticClassProvider | ClassProvider).useClass || provider.provide); if (hasDeps(provider)) { factory = () => new (classRef)(...injectArgs(provider.deps)); } else { diff --git a/packages/core/test/render3/component_spec.ts b/packages/core/test/render3/component_spec.ts index d04a78e65d..ca5170d050 100644 --- a/packages/core/test/render3/component_spec.ts +++ b/packages/core/test/render3/component_spec.ts @@ -708,20 +708,54 @@ describe('providers', () => { }); }); - it('forwardRef', (done) => { - setTimeout(() => { - expectProvidersScenario({ - parent: { - providers: [forwardRef(() => ForLater)], - componentAssertion: - () => { expect(directiveInject(ForLater) instanceof ForLater).toBeTruthy(); } - } - }); - done(); - }, 0); + describe('forwardRef', () => { + it('forwardRef resolves later', (done) => { + setTimeout(() => { + expectProvidersScenario({ + parent: { + providers: [forwardRef(() => ForLater)], + componentAssertion: + () => { expect(directiveInject(ForLater) instanceof ForLater).toBeTruthy(); } + } + }); + done(); + }, 0); + }); class ForLater {} + + // The following test that forwardRefs are called, so we don't search for an anon fn + it('ValueProvider wrapped in forwardRef', () => { + expectProvidersScenario({ + parent: { + providers: + [{provide: GREETER, useValue: forwardRef(() => { return {greet: 'Value'}; })}], + componentAssertion: () => { expect(directiveInject(GREETER).greet).toEqual('Value'); } + } + }); + }); + + it('ClassProvider wrapped in forwardRef', () => { + expectProvidersScenario({ + parent: { + providers: [{provide: GREETER, useClass: forwardRef(() => GreeterClass)}], + componentAssertion: () => { expect(directiveInject(GREETER).greet).toEqual('Class'); } + } + }); + }); + + it('ExistingProvider wrapped in forwardRef', () => { + expectProvidersScenario({ + parent: { + providers: + [GreeterClass, {provide: GREETER, useExisting: forwardRef(() => GreeterClass)}], + componentAssertion: () => { expect(directiveInject(GREETER).greet).toEqual('Class'); } + } + }); + }); + }); + }); /* @@ -1692,4 +1726,4 @@ function expectProvidersScenario(defs: { const fixture = new ComponentFixture( App, {injector: defs.ngModule ? createInjector(defs.ngModule) : undefined}); expect(fixture.html).toEqual('view-child'); -} \ No newline at end of file +}