From 1dacae2c3c53dff056828ec8ed1201f3eb170e01 Mon Sep 17 00:00:00 2001 From: Tobias Bosch Date: Sun, 24 Sep 2017 12:11:36 -0700 Subject: [PATCH] fix(compiler): work well with `forwardRef` with `useValue` / `useFactory` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The new expression lowering lowers everything after `useValue` / `useFactory` into a separate exported variable. If the value was a `forwardRef`, this was passed to the runtime and resulted in errors. This change unwraps `forwardRef`s during runtime again. Note: we can’t unwrap the `forwardRef` into an exported variable during compile time, as this would defeat the purpose of the `forwardRef` in referring to something that can’t be referred to at this position. --- .../integrationtest/src/features.ts | 4 +-- .../integrationtest/src/module.ts | 4 +-- .../integrationtest/test/forward_ref_spec.ts | 29 +++++++++++++++++++ packages/core/src/view/ng_module.ts | 5 ++++ packages/core/src/view/provider.ts | 6 +++- 5 files changed, 43 insertions(+), 5 deletions(-) create mode 100644 packages/compiler-cli/integrationtest/test/forward_ref_spec.ts diff --git a/packages/compiler-cli/integrationtest/src/features.ts b/packages/compiler-cli/integrationtest/src/features.ts index e6196dbd94..76802fc327 100644 --- a/packages/compiler-cli/integrationtest/src/features.ts +++ b/packages/compiler-cli/integrationtest/src/features.ts @@ -7,7 +7,7 @@ */ import * as common from '@angular/common'; -import {CUSTOM_ELEMENTS_SCHEMA, Component, Directive, EventEmitter, Inject, InjectionToken, NgModule, Output} from '@angular/core'; +import {CUSTOM_ELEMENTS_SCHEMA, Component, Directive, EventEmitter, Inject, InjectionToken, NgModule, Output, forwardRef} from '@angular/core'; import {Observable} from 'rxjs/Observable'; import {wrapInArray} from './funcs'; @@ -18,7 +18,7 @@ export const SOME_INJECTON_TOKEN = new InjectionToken('injectionToken'); selector: 'comp-providers', template: '', providers: [ - {provide: 'strToken', useValue: 'strValue'}, + {provide: 'strToken', useValue: forwardRef(() => 'strValue')}, {provide: SOME_INJECTON_TOKEN, useValue: 10}, {provide: 'reference', useValue: common.NgIf}, {provide: 'complexToken', useValue: {a: 1, b: ['test', SOME_INJECTON_TOKEN]}}, diff --git a/packages/compiler-cli/integrationtest/src/module.ts b/packages/compiler-cli/integrationtest/src/module.ts index f646fecf30..3398cb2ddc 100644 --- a/packages/compiler-cli/integrationtest/src/module.ts +++ b/packages/compiler-cli/integrationtest/src/module.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {ApplicationRef, NgModule} from '@angular/core'; +import {ApplicationRef, NgModule, forwardRef} from '@angular/core'; import {FormsModule} from '@angular/forms'; import {MATERIAL_SANITY_CHECKS, MdButtonModule} from '@angular/material'; import {ServerModule} from '@angular/platform-server'; @@ -68,7 +68,7 @@ export {SomeModule as JitSummariesSomeModule} from './jit_summaries'; ], providers: [ SomeService, - {provide: CUSTOM, useValue: {name: 'some name'}}, + {provide: CUSTOM, useValue: forwardRef(() => ({name: 'some name'}))}, // disable sanity check for material because it throws an error when used server-side // see https://github.com/angular/material2/issues/6292 {provide: MATERIAL_SANITY_CHECKS, useValue: false}, diff --git a/packages/compiler-cli/integrationtest/test/forward_ref_spec.ts b/packages/compiler-cli/integrationtest/test/forward_ref_spec.ts new file mode 100644 index 0000000000..e274e7b4f6 --- /dev/null +++ b/packages/compiler-cli/integrationtest/test/forward_ref_spec.ts @@ -0,0 +1,29 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import './init'; + +import * as fs from 'fs'; +import * as path from 'path'; + +import {CUSTOM} from '../src/custom_token'; +import {CompWithProviders} from '../src/features'; +import {MainModule} from '../src/module'; + +import {createComponent, createModule} from './util'; + +describe('template codegen output', () => { + it('should support forwardRef with useValue in components', () => { + const compFixture = createComponent(CompWithProviders); + expect(compFixture.componentInstance.ctxProp).toBe('strValue'); + }); + + it('should support forwardRef with useValue in modules', () => { + const modRef = createModule(); + expect(modRef.injector.get(CUSTOM).name).toBe('some name'); + }); +}); diff --git a/packages/core/src/view/ng_module.ts b/packages/core/src/view/ng_module.ts index c7c464aa34..25a5c3e715 100644 --- a/packages/core/src/view/ng_module.ts +++ b/packages/core/src/view/ng_module.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ +import {resolveForwardRef} from '../di/forward_ref'; import {Injector, THROW_IF_NOT_FOUND} from '../di/injector'; import {NgModuleRef} from '../linker/ng_module_factory'; @@ -20,6 +21,10 @@ const NgModuleRefTokenKey = tokenKey(NgModuleRef); export function moduleProvideDef( flags: NodeFlags, token: any, value: any, deps: ([DepFlags, any] | any)[]): NgModuleProviderDef { + // Need to resolve forwardRefs as e.g. for `useValue` we + // lowered the expression and then stopped evaluating it, + // i.e. also didn't unwrap it. + value = resolveForwardRef(value); const depDefs = splitDepsDsl(deps); return { // will bet set by the module definition diff --git a/packages/core/src/view/provider.ts b/packages/core/src/view/provider.ts index 798d599d79..93cfd2c611 100644 --- a/packages/core/src/view/provider.ts +++ b/packages/core/src/view/provider.ts @@ -7,7 +7,7 @@ */ import {ChangeDetectorRef, SimpleChange, SimpleChanges, WrappedValue} from '../change_detection/change_detection'; -import {Injector} from '../di'; +import {Injector, resolveForwardRef} from '../di'; import {ElementRef} from '../linker/element_ref'; import {TemplateRef} from '../linker/template_ref'; import {ViewContainerRef} from '../linker/view_container_ref'; @@ -77,6 +77,10 @@ export function _def( if (!bindings) { bindings = []; } + // Need to resolve forwardRefs as e.g. for `useValue` we + // lowered the expression and then stopped evaluating it, + // i.e. also didn't unwrap it. + value = resolveForwardRef(value); const depDefs = splitDepsDsl(deps);