From d47de60944e0715a6cf54ed54498fa5e4af32b73 Mon Sep 17 00:00:00 2001 From: Marc Laval Date: Thu, 28 Mar 2019 14:50:39 +0100 Subject: [PATCH] fix(ivy): @Component should support entry components from another module (#29566) PR Close #29566 --- packages/core/src/render3/jit/module.ts | 14 +------ packages/core/src/util/array_utils.ts | 4 +- .../core/test/acceptance/component_spec.ts | 41 ++++++++++++++++++- 3 files changed, 44 insertions(+), 15 deletions(-) diff --git a/packages/core/src/render3/jit/module.ts b/packages/core/src/render3/jit/module.ts index f1dc28b9bd..de29b27e51 100644 --- a/packages/core/src/render3/jit/module.ts +++ b/packages/core/src/render3/jit/module.ts @@ -14,6 +14,7 @@ import {Type} from '../../interface/type'; import {registerNgModuleType} from '../../linker/ng_module_factory_loader'; import {Component} from '../../metadata'; import {ModuleWithProviders, NgModule, NgModuleDef, NgModuleTransitiveScopes} from '../../metadata/ng_module'; +import {flatten} from '../../util/array_utils'; import {assertDefined} from '../../util/assert'; import {getComponentDef, getDirectiveDef, getNgModuleDef, getPipeDef} from '../definition'; import {NG_COMPONENT_DEF, NG_DIRECTIVE_DEF, NG_MODULE_DEF, NG_PIPE_DEF} from '../fields'; @@ -158,6 +159,7 @@ function verifySemanticsOfNgModuleDef(moduleType: NgModuleType): void { const errors: string[] = []; const declarations = maybeUnwrapFn(ngModuleDef.declarations); const imports = maybeUnwrapFn(ngModuleDef.imports); + flatten(imports, unwrapModuleWithProvidersImports).forEach(verifySemanticsOfNgModuleDef); const exports = maybeUnwrapFn(ngModuleDef.exports); declarations.forEach(verifyDeclarationsHaveDefinitions); const combinedDeclarations: Type[] = [ @@ -464,18 +466,6 @@ export function transitiveScopesFor( return scopes; } -function flatten(values: any[], mapFn?: (value: T) => any): Type[] { - const out: Type[] = []; - values.forEach(value => { - if (Array.isArray(value)) { - out.push(...flatten(value, mapFn)); - } else { - out.push(mapFn ? mapFn(value) : value); - } - }); - return out; -} - function expandModuleWithProviders(value: Type| ModuleWithProviders<{}>): Type { if (isModuleWithProviders(value)) { return value.ngModule; diff --git a/packages/core/src/util/array_utils.ts b/packages/core/src/util/array_utils.ts index 894da85195..aac3dba3d8 100644 --- a/packages/core/src/util/array_utils.ts +++ b/packages/core/src/util/array_utils.ts @@ -21,7 +21,7 @@ export function addAllToArray(items: any[], arr: any[]) { /** * Flattens an array in non-recursive way. Input arrays are not modified. */ -export function flatten(list: any[]): any[] { +export function flatten(list: any[], mapFn?: (value: any) => any): any[] { const result: any[] = []; let i = 0; while (i < list.length) { @@ -34,7 +34,7 @@ export function flatten(list: any[]): any[] { i++; } } else { - result.push(item); + result.push(mapFn ? mapFn(item) : item); i++; } } diff --git a/packages/core/test/acceptance/component_spec.ts b/packages/core/test/acceptance/component_spec.ts index c5c608f479..db0f0f73fa 100644 --- a/packages/core/test/acceptance/component_spec.ts +++ b/packages/core/test/acceptance/component_spec.ts @@ -6,8 +6,9 @@ * found in the LICENSE file at https://angular.io/license */ -import {Component, InjectionToken} from '@angular/core'; +import {Component, ComponentFactoryResolver, ComponentRef, InjectionToken, NgModule, Type, ViewChild, ViewContainerRef} from '@angular/core'; import {TestBed} from '@angular/core/testing'; +import {expect} from '@angular/platform-browser/testing/src/matchers'; describe('component', () => { @@ -49,4 +50,42 @@ describe('component', () => { expect(destroyCalls).toBe(1, 'Expected `ngOnDestroy` to only be called once.'); }); }); + + it('should support entry components from another module', () => { + @Component({selector: 'other-component', template: `bar`}) + class OtherComponent { + } + + @NgModule({ + declarations: [OtherComponent], + exports: [OtherComponent], + entryComponents: [OtherComponent] + }) + class OtherModule { + } + + @Component({ + selector: 'test_component', + template: `foo|`, + entryComponents: [OtherComponent] + }) + class TestComponent { + @ViewChild('vc', {read: ViewContainerRef}) vcref !: ViewContainerRef; + + constructor(private _cfr: ComponentFactoryResolver) {} + + createComponentView(cmptType: Type): ComponentRef { + const cf = this._cfr.resolveComponentFactory(cmptType); + return this.vcref.createComponent(cf); + } + } + + TestBed.configureTestingModule({declarations: [TestComponent], imports: [OtherModule]}); + const fixture = TestBed.createComponent(TestComponent); + fixture.detectChanges(); + + fixture.componentInstance.createComponentView(OtherComponent); + fixture.detectChanges(); + expect(fixture.nativeElement).toHaveText('foo|bar'); + }); });