diff --git a/packages/core/src/render3/component_ref.ts b/packages/core/src/render3/component_ref.ts index be48fabeed..18318a9a64 100644 --- a/packages/core/src/render3/component_ref.ts +++ b/packages/core/src/render3/component_ref.ts @@ -24,11 +24,11 @@ import {assertComponentType} from './assert'; import {LifecycleHooksFeature, createRootComponent, createRootComponentView, createRootContext} from './component'; import {getComponentDef} from './definition'; import {NodeInjector} from './di'; -import {addToViewTree, createLView, createNodeAtIndex, createTView, createViewNode, elementCreate, locateHostElement, refreshDescendantViews} from './instructions'; -import {ComponentDef, RenderFlags} from './interfaces/definition'; -import {TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeType} from './interfaces/node'; -import {RElement, RendererFactory3, domRendererFactory3, isProceduralRenderer} from './interfaces/renderer'; -import {HEADER_OFFSET, LView, LViewFlags, RootContext, TVIEW} from './interfaces/view'; +import {addToViewTree, createLView, createTView, createViewNode, elementCreate, locateHostElement, refreshDescendantViews} from './instructions'; +import {ComponentDef} from './interfaces/definition'; +import {TContainerNode, TElementContainerNode, TElementNode} from './interfaces/node'; +import {RNode, RendererFactory3, domRendererFactory3, isProceduralRenderer} from './interfaces/renderer'; +import {HEADER_OFFSET, LView, LViewFlags, RootContext} from './interfaces/view'; import {enterView, leaveView} from './state'; import {defaultScheduler, getTNode} from './util'; import {createElementRef} from './view_engine_compatibility'; @@ -174,34 +174,12 @@ export class ComponentFactory extends viewEngine_ComponentFactory { tElementNode = getTNode(0, rootLView) as TElementNode; - // Transform the arrays of native nodes into a structure that can be consumed by the - // projection instruction. This is needed to support the reprojection of these nodes. if (projectableNodes) { - let index = 0; - const tView = rootLView[TVIEW]; - const projection: TNode[] = tElementNode.projection = []; - for (let i = 0; i < projectableNodes.length; i++) { - const nodeList = projectableNodes[i]; - let firstTNode: TNode|null = null; - let previousTNode: TNode|null = null; - for (let j = 0; j < nodeList.length; j++) { - if (tView.firstTemplatePass) { - // For dynamically created components such as ComponentRef, we create a new TView for - // each insert. This is not ideal since we should be sharing the TViews. - // Also the logic here should be shared with `component.ts`'s `renderComponent` - // method. - tView.expandoStartIndex++; - tView.blueprint.splice(++index + HEADER_OFFSET, 0, null); - tView.data.splice(index + HEADER_OFFSET, 0, null); - rootLView.splice(index + HEADER_OFFSET, 0, null); - } - const tNode = - createNodeAtIndex(index, TNodeType.Element, nodeList[j] as RElement, null, null); - previousTNode ? (previousTNode.next = tNode) : (firstTNode = tNode); - previousTNode = tNode; - } - projection.push(firstTNode !); - } + // projectable nodes can be passed as array of arrays or an array of iterables (ngUpgrade + // case). Here we do normalize passed data structure to be an array of arrays to avoid + // complex checks down the line. + tElementNode.projection = + projectableNodes.map((nodesforSlot: RNode[]) => { return Array.from(nodesforSlot); }); } // TODO: should LifecycleHooksFeature and other host features be generated by the compiler and diff --git a/packages/core/src/render3/instructions.ts b/packages/core/src/render3/instructions.ts index 38b2541fb7..fbe0a010d2 100644 --- a/packages/core/src/render3/instructions.ts +++ b/packages/core/src/render3/instructions.ts @@ -2471,36 +2471,44 @@ export function projection(nodeIndex: number, selectorIndex: number = 0, attrs?: let projectedView = componentView[PARENT] !; let projectionNodeIndex = -1; - while (nodeToProject) { - if (nodeToProject.type === TNodeType.Projection) { - // This node is re-projected, so we must go up the tree to get its projected nodes. - const currentComponentView = findComponentView(projectedView); - const currentComponentHost = currentComponentView[HOST_NODE] as TElementNode; - const firstProjectedNode = - (currentComponentHost.projection as(TNode | null)[])[nodeToProject.projection as number]; + if (Array.isArray(nodeToProject)) { + appendChild(nodeToProject, tProjectionNode, lView); + } else { + while (nodeToProject) { + if (nodeToProject.type === TNodeType.Projection) { + // This node is re-projected, so we must go up the tree to get its projected nodes. + const currentComponentView = findComponentView(projectedView); + const currentComponentHost = currentComponentView[HOST_NODE] as TElementNode; + const firstProjectedNode = (currentComponentHost.projection as( + TNode | null)[])[nodeToProject.projection as number]; - if (firstProjectedNode) { - projectionNodeStack[++projectionNodeIndex] = nodeToProject; - projectionNodeStack[++projectionNodeIndex] = projectedView; + if (firstProjectedNode) { + if (Array.isArray(firstProjectedNode)) { + appendChild(firstProjectedNode, tProjectionNode, lView); + } else { + projectionNodeStack[++projectionNodeIndex] = nodeToProject; + projectionNodeStack[++projectionNodeIndex] = projectedView; - nodeToProject = firstProjectedNode; - projectedView = currentComponentView[PARENT] !; - continue; + nodeToProject = firstProjectedNode; + projectedView = currentComponentView[PARENT] !; + continue; + } + } + } else { + // This flag must be set now or we won't know that this node is projected + // if the nodes are inserted into a container later. + nodeToProject.flags |= TNodeFlags.isProjected; + appendProjectedNode(nodeToProject, tProjectionNode, lView, projectedView); } - } else { - // This flag must be set now or we won't know that this node is projected - // if the nodes are inserted into a container later. - nodeToProject.flags |= TNodeFlags.isProjected; - appendProjectedNode(nodeToProject, tProjectionNode, lView, projectedView); - } - // If we are finished with a list of re-projected nodes, we need to get - // back to the root projection node that was re-projected. - if (nodeToProject.next === null && projectedView !== componentView[PARENT] !) { - projectedView = projectionNodeStack[projectionNodeIndex--] as LView; - nodeToProject = projectionNodeStack[projectionNodeIndex--] as TNode; + // If we are finished with a list of re-projected nodes, we need to get + // back to the root projection node that was re-projected. + if (nodeToProject.next === null && projectedView !== componentView[PARENT] !) { + projectedView = projectionNodeStack[projectionNodeIndex--] as LView; + nodeToProject = projectionNodeStack[projectionNodeIndex--] as TNode; + } + nodeToProject = nodeToProject.next; } - nodeToProject = nodeToProject.next; } } diff --git a/packages/core/src/render3/interfaces/node.ts b/packages/core/src/render3/interfaces/node.ts index 07a6ed6971..a86ac83727 100644 --- a/packages/core/src/render3/interfaces/node.ts +++ b/packages/core/src/render3/interfaces/node.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ +import {RNode} from './renderer'; import {StylingContext} from './styling'; import {LView, TView} from './view'; @@ -333,8 +334,11 @@ export interface TNode { * `getHost(currentTNode).projection[currentTNode.projection]`. * - When projecting nodes the parent node retrieved may be a `` node, in which case * the process is recursive in nature (not implementation). + * + * If `projection` is of type `RNode[][]` than we have a collection of native nodes passed as + * projectable nodes during dynamic component creation. */ - projection: (TNode|null)[]|number|null; + projection: (TNode|RNode[])[]|number|null; } /** Static data for an element */ @@ -352,10 +356,10 @@ export interface TElementNode extends TNode { /** * If this is a component TNode with projection, this will be an array of projected - * TNodes (see TNode.projection for more info). If it's a regular element node or a - * component without projection, it will be null. + * TNodes or native nodes (see TNode.projection for more info). If it's a regular element node or + * a component without projection, it will be null. */ - projection: (TNode|null)[]|null; + projection: (TNode|RNode[])[]|null; } /** Static data for a text node */ diff --git a/packages/core/src/render3/node_manipulation.ts b/packages/core/src/render3/node_manipulation.ts index 62be5b49b3..79ec137a94 100644 --- a/packages/core/src/render3/node_manipulation.ts +++ b/packages/core/src/render3/node_manipulation.ts @@ -9,7 +9,7 @@ import {attachPatchData} from './context_discovery'; import {callHooks} from './hooks'; import {LContainer, NATIVE, VIEWS, unusedValueExportToPlacateAjd as unused1} from './interfaces/container'; -import {TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeType, TViewNode, unusedValueExportToPlacateAjd as unused2} from './interfaces/node'; +import {TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeType, TProjectionNode, TViewNode, unusedValueExportToPlacateAjd as unused2} from './interfaces/node'; import {unusedValueExportToPlacateAjd as unused3} from './interfaces/projection'; import {ProceduralRenderer3, RComment, RElement, RNode, RText, Renderer3, isProceduralRenderer, unusedValueExportToPlacateAjd as unused4} from './interfaces/renderer'; import {CLEANUP, CONTAINER_INDEX, FLAGS, HEADER_OFFSET, HOST_NODE, HookData, LView, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, TVIEW, unusedValueExportToPlacateAjd as unused5} from './interfaces/view'; @@ -109,14 +109,22 @@ function walkTNodeTree( const head: TNode|null = (componentHost.projection as(TNode | null)[])[tNode.projection as number]; - // Must store both the TNode and the view because this projection node could be nested - // deeply inside embedded views, and we need to get back down to this particular nested view. - projectionNodeStack[++projectionNodeIndex] = tNode; - projectionNodeStack[++projectionNodeIndex] = currentView !; - if (head) { - currentView = componentView[PARENT] !; - nextTNode = currentView[TVIEW].data[head.index] as TNode; + if (Array.isArray(head)) { + for (let nativeNode of head) { + executeNodeAction(action, renderer, renderParent, nativeNode, tNode, beforeNode); + } + } else { + // Must store both the TNode and the view because this projection node could be nested + // deeply inside embedded views, and we need to get back down to this particular nested + // view. + projectionNodeStack[++projectionNodeIndex] = tNode; + projectionNodeStack[++projectionNodeIndex] = currentView !; + if (head) { + currentView = componentView[PARENT] !; + nextTNode = currentView[TVIEW].data[head.index] as TNode; + } } + } else { // Otherwise, this is a View or an ElementContainer nextTNode = tNode.child; @@ -548,6 +556,23 @@ export function nativeInsertBefore( } } +function nativeAppendChild(renderer: Renderer3, parent: RElement, child: RNode): void { + if (isProceduralRenderer(renderer)) { + renderer.appendChild(parent, child); + } else { + parent.appendChild(child); + } +} + +function nativeAppendOrInsertBefore( + renderer: Renderer3, parent: RElement, child: RNode, beforeNode: RNode | null) { + if (beforeNode) { + nativeInsertBefore(renderer, parent, child, beforeNode); + } else { + nativeAppendChild(renderer, parent, child); + } +} + /** * Removes a native child node from a given native parent node. */ @@ -572,35 +597,47 @@ export function nativeNextSibling(renderer: Renderer3, node: RNode): RNode|null } /** - * Appends the `child` element to the `parent`. + * Finds a native "anchor" node for cases where we can't append a native child directly + * (`appendChild`) and need to use a reference (anchor) node for the `insertBefore` operation. + * @param parentTNode + * @param lView + */ +function getNativeAnchorNode(parentTNode: TNode, lView: LView): RNode|null { + if (parentTNode.type === TNodeType.View) { + const lContainer = getLContainer(parentTNode as TViewNode, lView) !; + const views = lContainer[VIEWS]; + const index = views.indexOf(lView); + return getBeforeNodeForView(index, views, lContainer[NATIVE]); + } else if ( + parentTNode.type === TNodeType.ElementContainer || + parentTNode.type === TNodeType.IcuContainer) { + return getNativeByTNode(parentTNode, lView); + } + return null; +} + +/** + * Appends the `child` native node (or a collection of nodes) to the `parent`. * * The element insertion might be delayed {@link canInsertNativeNode}. * - * @param childEl The child that should be appended + * @param childEl The native child (or children) that should be appended * @param childTNode The TNode of the child element * @param currentView The current LView * @returns Whether or not the child was appended */ -export function appendChild(childEl: RNode, childTNode: TNode, currentView: LView): void { +export function appendChild(childEl: RNode | RNode[], childTNode: TNode, currentView: LView): void { const renderParent = getRenderParent(childTNode, currentView); if (renderParent != null) { const renderer = currentView[RENDERER]; const parentTNode: TNode = childTNode.parent || currentView[HOST_NODE] !; - - if (parentTNode.type === TNodeType.View) { - const lContainer = getLContainer(parentTNode as TViewNode, currentView) !; - const views = lContainer[VIEWS]; - const index = views.indexOf(currentView); - nativeInsertBefore( - renderer, renderParent, childEl, getBeforeNodeForView(index, views, lContainer[NATIVE])); - } else if ( - parentTNode.type === TNodeType.ElementContainer || - parentTNode.type === TNodeType.IcuContainer) { - const anchorNode = getNativeByTNode(parentTNode, currentView); - nativeInsertBefore(renderer, renderParent, childEl, anchorNode); + const anchorNode = getNativeAnchorNode(parentTNode, currentView); + if (Array.isArray(childEl)) { + for (let nativeNode of childEl) { + nativeAppendOrInsertBefore(renderer, renderParent, nativeNode, anchorNode); + } } else { - isProceduralRenderer(renderer) ? renderer.appendChild(renderParent, childEl) : - renderParent.appendChild(childEl); + nativeAppendOrInsertBefore(renderer, renderParent, childEl, anchorNode); } } } diff --git a/packages/core/test/bundling/hello_world/bundle.golden_symbols.json b/packages/core/test/bundling/hello_world/bundle.golden_symbols.json index 28212a2e4d..bff40dcc6f 100644 --- a/packages/core/test/bundling/hello_world/bundle.golden_symbols.json +++ b/packages/core/test/bundling/hello_world/bundle.golden_symbols.json @@ -119,6 +119,9 @@ { "name": "__self" }, + { + "name": "__values" + }, { "name": "__window" }, @@ -254,6 +257,9 @@ { "name": "getLViewChild" }, + { + "name": "getNativeAnchorNode" + }, { "name": "getNativeByTNode" }, @@ -344,6 +350,12 @@ { "name": "namespaceHTML" }, + { + "name": "nativeAppendChild" + }, + { + "name": "nativeAppendOrInsertBefore" + }, { "name": "nativeInsertBefore" }, diff --git a/packages/core/test/bundling/todo/bundle.golden_symbols.json b/packages/core/test/bundling/todo/bundle.golden_symbols.json index 38ec613887..442a8dc109 100644 --- a/packages/core/test/bundling/todo/bundle.golden_symbols.json +++ b/packages/core/test/bundling/todo/bundle.golden_symbols.json @@ -275,6 +275,9 @@ { "name": "__spread" }, + { + "name": "__values" + }, { "name": "__window" }, @@ -704,6 +707,9 @@ { "name": "getMultiStartIndex" }, + { + "name": "getNativeAnchorNode" + }, { "name": "getNativeByIndex" }, @@ -983,6 +989,12 @@ { "name": "namespaceHTML" }, + { + "name": "nativeAppendChild" + }, + { + "name": "nativeAppendOrInsertBefore" + }, { "name": "nativeInsertBefore" }, diff --git a/packages/core/test/linker/projection_integration_spec.ts b/packages/core/test/linker/projection_integration_spec.ts index 91a48c28b4..1b52edfafc 100644 --- a/packages/core/test/linker/projection_integration_spec.ts +++ b/packages/core/test/linker/projection_integration_spec.ts @@ -6,8 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {Component, Directive, ElementRef, TemplateRef, ViewContainerRef, ViewEncapsulation} from '@angular/core'; -import {TestBed} from '@angular/core/testing'; +import {Component, ComponentFactoryResolver, ComponentRef, Directive, ElementRef, Injector, NgModule, OnInit, TemplateRef, ViewChild, ViewContainerRef, ViewEncapsulation} from '@angular/core'; +import {ComponentFixture, TestBed} from '@angular/core/testing'; import {By} from '@angular/platform-browser/src/dom/debug/by'; import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; import {expect} from '@angular/platform-browser/testing/src/matchers'; @@ -611,6 +611,121 @@ describe('projection', () => { main.detectChanges(); expect(main.nativeElement).toHaveText('(, D)'); }); + + describe('projectable nodes', () => { + + @Component({selector: 'test', template: ''}) + class TestComponent { + constructor(public cfr: ComponentFactoryResolver) {} + } + + @Component({selector: 'with-content', template: ''}) + class WithContentCmpt { + @ViewChild('ref') directiveRef: any; + } + + @Component({selector: 're-project', template: ''}) + class ReProjectCmpt { + } + + @Directive({selector: '[insert]'}) + class InsertTplRef implements OnInit { + constructor(private _vcRef: ViewContainerRef, private _tplRef: TemplateRef<{}>) {} + + ngOnInit() { this._vcRef.createEmbeddedView(this._tplRef); } + } + + @Directive({selector: '[delayedInsert]', exportAs: 'delayedInsert'}) + class DelayedInsertTplRef { + constructor(public vc: ViewContainerRef, public templateRef: TemplateRef) {} + show() { this.vc.createEmbeddedView(this.templateRef); } + hide() { this.vc.clear(); } + } + + @NgModule({ + declarations: [WithContentCmpt, InsertTplRef, DelayedInsertTplRef, ReProjectCmpt], + entryComponents: [WithContentCmpt] + }) + class TestModule { + } + + let fixture: ComponentFixture; + + function createCmptInstance( + tpl: string, projectableNodes: any[][]): ComponentRef { + TestBed.configureTestingModule({declarations: [TestComponent], imports: [TestModule]}); + TestBed.overrideTemplate(WithContentCmpt, tpl); + + fixture = TestBed.createComponent(TestComponent); + const cfr = fixture.componentInstance.cfr; + const cf = cfr.resolveComponentFactory(WithContentCmpt); + const cmptRef = cf.create(Injector.NULL, projectableNodes); + + cmptRef.changeDetectorRef.detectChanges(); + + return cmptRef; + } + + it('should pass nodes to the default ng-content without selectors', () => { + const cmptRef = createCmptInstance( + '
()
', [[document.createTextNode('A')]]); + expect(cmptRef.location.nativeElement).toHaveText('(A)'); + }); + + it('should pass nodes to the default ng-content at the root', () => { + const cmptRef = + createCmptInstance('', [[document.createTextNode('A')]]); + expect(cmptRef.location.nativeElement).toHaveText('A'); + }); + + it('should pass nodes to multiple ng-content tags', () => { + const cmptRef = createCmptInstance( + 'A:()B:()C:()', + [ + [document.createTextNode('A')], [document.createTextNode('B')], + [document.createTextNode('C')] + ]); + expect(cmptRef.location.nativeElement).toHaveText('A:(A)B:(B)C:(C)'); + }); + + it('should pass nodes to the default ng-content inside ng-container', () => { + const cmptRef = createCmptInstance( + 'A()C', + [[document.createTextNode('B')]]); + expect(cmptRef.location.nativeElement).toHaveText('A(B)C'); + }); + + it('should pass nodes to the default ng-content inside an embedded view', () => { + const cmptRef = createCmptInstance( + 'A()C', + [[document.createTextNode('B')]]); + expect(cmptRef.location.nativeElement).toHaveText('A(B)C'); + }); + + it('should pass nodes to the default ng-content inside a delayed embedded view', () => { + const cmptRef = createCmptInstance( + 'A([])C', + [[document.createTextNode('B')]]); + expect(cmptRef.location.nativeElement).toHaveText('A()C'); + + const delayedInsert = cmptRef.instance.directiveRef as DelayedInsertTplRef; + + delayedInsert.show(); + cmptRef.changeDetectorRef.detectChanges(); + expect(cmptRef.location.nativeElement).toHaveText('A([B])C'); + + delayedInsert.hide(); + cmptRef.changeDetectorRef.detectChanges(); + expect(cmptRef.location.nativeElement).toHaveText('A()C'); + }); + + it('should re-project at the root', () => { + const cmptRef = createCmptInstance( + 'A[()]C', + [[document.createTextNode('B')]]); + expect(cmptRef.location.nativeElement).toHaveText('A[(B)]C'); + }); + }); }); @Component({selector: 'main', template: ''}) diff --git a/packages/core/test/render3/renderer_factory_spec.ts b/packages/core/test/render3/renderer_factory_spec.ts index 42fc1f4102..b32da94ffa 100644 --- a/packages/core/test/render3/renderer_factory_spec.ts +++ b/packages/core/test/render3/renderer_factory_spec.ts @@ -13,7 +13,6 @@ import {RendererType2, ViewEncapsulation} from '../../src/core'; import {defineComponent} from '../../src/render3/index'; import {bind, container, containerRefreshEnd, containerRefreshStart, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, listener, text, tick} from '../../src/render3/instructions'; import {RenderFlags} from '../../src/render3/interfaces/definition'; -import {createRendererType2} from '../../src/view/index'; import {getAnimationRendererFactory2, getRendererFactory2} from './imported_renderer2'; import {TemplateFixture, containerEl, document, renderComponent, renderToHtml, toHtml} from './render_util'; diff --git a/packages/upgrade/test/dynamic/upgrade_spec.ts b/packages/upgrade/test/dynamic/upgrade_spec.ts index 11160bd777..32e8626355 100644 --- a/packages/upgrade/test/dynamic/upgrade_spec.ts +++ b/packages/upgrade/test/dynamic/upgrade_spec.ts @@ -10,7 +10,6 @@ import {ChangeDetectorRef, Component, EventEmitter, Input, NO_ERRORS_SCHEMA, NgM import {async, fakeAsync, flushMicrotasks, tick} from '@angular/core/testing'; import {BrowserModule} from '@angular/platform-browser'; import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; -import {fixmeIvy} from '@angular/private/testing'; import * as angular from '@angular/upgrade/src/common/angular1'; import {$EXCEPTION_HANDLER} from '@angular/upgrade/src/common/constants'; import {UpgradeAdapter, UpgradeAdapterRef} from '@angular/upgrade/src/dynamic/upgrade_adapter'; @@ -3098,33 +3097,31 @@ withEachNg1Version(() => { }); })); - fixmeIvy('FW-873: projected component injector hierarchy not wired up correctly') - .it('should respect hierarchical dependency injection for ng2', async(() => { - const ng1Module = angular.module('ng1', []); + it('should respect hierarchical dependency injection for ng2', async(() => { + const ng1Module = angular.module('ng1', []); - @Component( - {selector: 'ng2-parent', template: `ng2-parent()`}) - class Ng2Parent { - } - @Component({selector: 'ng2-child', template: `ng2-child`}) - class Ng2Child { - constructor(parent: Ng2Parent) {} - } + @Component({selector: 'ng2-parent', template: `ng2-parent()`}) + class Ng2Parent { + } + @Component({selector: 'ng2-child', template: `ng2-child`}) + class Ng2Child { + constructor(parent: Ng2Parent) {} + } - @NgModule({declarations: [Ng2Parent, Ng2Child], imports: [BrowserModule]}) - class Ng2Module { - } + @NgModule({declarations: [Ng2Parent, Ng2Child], imports: [BrowserModule]}) + class Ng2Module { + } - const element = html(''); + const element = html(''); - const adapter: UpgradeAdapter = new UpgradeAdapter(Ng2Module); - ng1Module.directive('ng2Parent', adapter.downgradeNg2Component(Ng2Parent)) - .directive('ng2Child', adapter.downgradeNg2Component(Ng2Child)); - adapter.bootstrap(element, ['ng1']).ready((ref) => { - expect(document.body.textContent).toEqual('ng2-parent(ng2-child)'); - ref.dispose(); - }); - })); + const adapter: UpgradeAdapter = new UpgradeAdapter(Ng2Module); + ng1Module.directive('ng2Parent', adapter.downgradeNg2Component(Ng2Parent)) + .directive('ng2Child', adapter.downgradeNg2Component(Ng2Child)); + adapter.bootstrap(element, ['ng1']).ready((ref) => { + expect(document.body.textContent).toEqual('ng2-parent(ng2-child)'); + ref.dispose(); + }); + })); }); describe('testability', () => { diff --git a/packages/upgrade/test/static/integration/downgrade_component_spec.ts b/packages/upgrade/test/static/integration/downgrade_component_spec.ts index 5ca71fd7fc..fe78f26f03 100644 --- a/packages/upgrade/test/static/integration/downgrade_component_spec.ts +++ b/packages/upgrade/test/static/integration/downgrade_component_spec.ts @@ -705,37 +705,36 @@ withEachNg1Version(() => { }); })); - fixmeIvy('FW-873: projected component injector hierarchy not wired up correctly') - .it('should respect hierarchical dependency injection for ng2', async(() => { - @Component({selector: 'parent', template: 'parent()'}) - class ParentComponent { - } + it('should respect hierarchical dependency injection for ng2', async(() => { + @Component({selector: 'parent', template: 'parent()'}) + class ParentComponent { + } - @Component({selector: 'child', template: 'child'}) - class ChildComponent { - constructor(parent: ParentComponent) {} - } + @Component({selector: 'child', template: 'child'}) + class ChildComponent { + constructor(parent: ParentComponent) {} + } - @NgModule({ - declarations: [ParentComponent, ChildComponent], - entryComponents: [ParentComponent, ChildComponent], - imports: [BrowserModule, UpgradeModule] - }) - class Ng2Module { - ngDoBootstrap() {} - } + @NgModule({ + declarations: [ParentComponent, ChildComponent], + entryComponents: [ParentComponent, ChildComponent], + imports: [BrowserModule, UpgradeModule] + }) + class Ng2Module { + ngDoBootstrap() {} + } - const ng1Module = - angular.module('ng1', []) - .directive('parent', downgradeComponent({component: ParentComponent})) - .directive('child', downgradeComponent({component: ChildComponent})); + const ng1Module = + angular.module('ng1', []) + .directive('parent', downgradeComponent({component: ParentComponent})) + .directive('child', downgradeComponent({component: ChildComponent})); - const element = html(''); + const element = html(''); - bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(upgrade => { - expect(multiTrim(document.body.textContent)).toBe('parent(child)'); - }); - })); + bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(upgrade => { + expect(multiTrim(document.body.textContent)).toBe('parent(child)'); + }); + })); fixmeIvy( 'FW-717: Injector on lazy loaded components are not the same as their NgModule\'s injector') diff --git a/packages/upgrade/test/static/integration/downgrade_module_spec.ts b/packages/upgrade/test/static/integration/downgrade_module_spec.ts index 4924cc8b73..663158ae11 100644 --- a/packages/upgrade/test/static/integration/downgrade_module_spec.ts +++ b/packages/upgrade/test/static/integration/downgrade_module_spec.ts @@ -330,93 +330,91 @@ withEachNg1Version(() => { expect(multiTrim(element.children[1].textContent)).toBe('Counter:1'); })); - fixmeIvy('FW-873: projected component injector hierarchy not wired up correctly') - .it('should correctly traverse the injector tree of downgraded components', async(() => { - @Component({ - selector: 'ng2A', - template: 'ng2A()', - providers: [ - {provide: 'FOO', useValue: 'CompA-foo'}, - {provide: 'BAR', useValue: 'CompA-bar'}, - ], - }) - class Ng2ComponentA { - } + it('should correctly traverse the injector tree of downgraded components', async(() => { + @Component({ + selector: 'ng2A', + template: 'ng2A()', + providers: [ + {provide: 'FOO', useValue: 'CompA-foo'}, + {provide: 'BAR', useValue: 'CompA-bar'}, + ], + }) + class Ng2ComponentA { + } - @Component({ - selector: 'ng2B', - template: ` + @Component({ + selector: 'ng2B', + template: ` FOO:{{ foo }} BAR:{{ bar }} BAZ:{{ baz }} QUX:{{ qux }} `, - providers: [ - {provide: 'FOO', useValue: 'CompB-foo'}, - ], - }) - class Ng2ComponentB { - constructor( - @Inject('FOO') public foo: string, @Inject('BAR') public bar: string, - @Inject('BAZ') public baz: string, @Inject('QUX') public qux: string) {} - } + providers: [ + {provide: 'FOO', useValue: 'CompB-foo'}, + ], + }) + class Ng2ComponentB { + constructor( + @Inject('FOO') public foo: string, @Inject('BAR') public bar: string, + @Inject('BAZ') public baz: string, @Inject('QUX') public qux: string) {} + } - @NgModule({ - declarations: [Ng2ComponentA, Ng2ComponentB], - entryComponents: [Ng2ComponentA, Ng2ComponentB], - imports: [BrowserModule], - providers: [ - {provide: 'FOO', useValue: 'Mod-foo'}, - {provide: 'BAR', useValue: 'Mod-bar'}, - {provide: 'BAZ', useValue: 'Mod-baz'}, - ], - }) - class Ng2Module { - ngDoBootstrap() {} - } + @NgModule({ + declarations: [Ng2ComponentA, Ng2ComponentB], + entryComponents: [Ng2ComponentA, Ng2ComponentB], + imports: [BrowserModule], + providers: [ + {provide: 'FOO', useValue: 'Mod-foo'}, + {provide: 'BAR', useValue: 'Mod-bar'}, + {provide: 'BAZ', useValue: 'Mod-baz'}, + ], + }) + class Ng2Module { + ngDoBootstrap() {} + } - const bootstrapFn = (extraProviders: StaticProvider[]) => { - const platformRef = getPlatform() || platformBrowserDynamic([ - ...extraProviders, - {provide: 'FOO', useValue: 'Plat-foo'}, - {provide: 'BAR', useValue: 'Plat-bar'}, - {provide: 'BAZ', useValue: 'Plat-baz'}, - {provide: 'QUX', useValue: 'Plat-qux'}, - ]); - return platformRef.bootstrapModule(Ng2Module); - }; + const bootstrapFn = (extraProviders: StaticProvider[]) => { + const platformRef = getPlatform() || platformBrowserDynamic([ + ...extraProviders, + {provide: 'FOO', useValue: 'Plat-foo'}, + {provide: 'BAR', useValue: 'Plat-bar'}, + {provide: 'BAZ', useValue: 'Plat-baz'}, + {provide: 'QUX', useValue: 'Plat-qux'}, + ]); + return platformRef.bootstrapModule(Ng2Module); + }; - const downMod = downgradeModule(bootstrapFn); - const ng1Module = - angular.module('ng1', [downMod]) - .directive( - 'ng2A', downgradeComponent({component: Ng2ComponentA, propagateDigest})) - .directive( - 'ng2B', - downgradeComponent({component: Ng2ComponentB, propagateDigest})); + const downMod = downgradeModule(bootstrapFn); + const ng1Module = + angular.module('ng1', [downMod]) + .directive( + 'ng2A', downgradeComponent({component: Ng2ComponentA, propagateDigest})) + .directive( + 'ng2B', downgradeComponent({component: Ng2ComponentB, propagateDigest})); - const element = html(` + const element = html(` `); - const $injector = angular.bootstrap(element, [ng1Module.name]); - const $rootScope = $injector.get($ROOT_SCOPE) as angular.IRootScopeService; + const $injector = angular.bootstrap(element, [ng1Module.name]); + const $rootScope = $injector.get($ROOT_SCOPE) as angular.IRootScopeService; - // Wait for the module to be bootstrapped. - setTimeout(() => { - expect(multiTrim(element.textContent)).toBe('ng2A()'); + // Wait for the module to be bootstrapped. + setTimeout(() => { + expect(multiTrim(element.textContent)).toBe('ng2A()'); - // Nested component B. - $rootScope.$apply('showB1 = true'); - expect(multiTrim(element.children[0].textContent)) - .toBe('ng2A( FOO:CompB-foo BAR:CompA-bar BAZ:Mod-baz QUX:Plat-qux )'); + // Nested component B. + $rootScope.$apply('showB1 = true'); + expect(multiTrim(element.children[0].textContent)) + .toBe('ng2A( FOO:CompB-foo BAR:CompA-bar BAZ:Mod-baz QUX:Plat-qux )'); - // Standalone component B. - $rootScope.$apply('showB2 = true'); - expect(multiTrim(element.children[1].textContent)) - .toBe('FOO:CompB-foo BAR:Mod-bar BAZ:Mod-baz QUX:Plat-qux'); - }); - })); + // Standalone component B. + $rootScope.$apply('showB2 = true'); + expect(multiTrim(element.children[1].textContent)) + .toBe('FOO:CompB-foo BAR:Mod-bar BAZ:Mod-baz QUX:Plat-qux'); + }); + })); fixmeIvy('FW-873: projected component injector hierarchy not wired up correctly') .it('should correctly traverse the injector tree of downgraded components (from different modules)',