diff --git a/integration/_payload-limits.json b/integration/_payload-limits.json
index de6ba07073..a077c6a074 100644
--- a/integration/_payload-limits.json
+++ b/integration/_payload-limits.json
@@ -12,7 +12,7 @@
"master": {
"uncompressed": {
"runtime-es2015": 1485,
- "main-es2015": 14786,
+ "main-es2015": 15040,
"polyfills-es2015": 36808
}
}
@@ -39,7 +39,7 @@
"master": {
"uncompressed": {
"runtime-es2015": 2289,
- "main-es2015": 268331,
+ "main-es2015": 268404,
"polyfills-es2015": 36808,
"5-es2015": 751
}
@@ -64,4 +64,4 @@
}
}
}
-}
+}
\ No newline at end of file
diff --git a/packages/core/src/render3/node_manipulation.ts b/packages/core/src/render3/node_manipulation.ts
index 246b30b2c9..a7a0b13d44 100644
--- a/packages/core/src/render3/node_manipulation.ts
+++ b/packages/core/src/render3/node_manipulation.ts
@@ -20,8 +20,8 @@ import {ProceduralRenderer3, RElement, RNode, RText, Renderer3, isProceduralRend
import {isLContainer, isLView, isRootView} from './interfaces/type_checks';
import {CHILD_HEAD, CLEANUP, DECLARATION_LCONTAINER, FLAGS, HOST, HookData, LView, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, TVIEW, T_HOST, unusedValueExportToPlacateAjd as unused5} from './interfaces/view';
import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert';
-import {findComponentView} from './util/view_traversal_utils';
-import {getNativeByTNode, getNativeByTNodeOrNull, unwrapRNode} from './util/view_utils';
+import {findComponentView, getLViewParent} from './util/view_traversal_utils';
+import {getNativeByTNode, unwrapRNode} from './util/view_utils';
const unusedValueToPlacateAjd = unused1 + unused2 + unused3 + unused4 + unused5;
@@ -652,25 +652,55 @@ export function appendChild(childEl: RNode | RNode[], childTNode: TNode, current
}
}
+/**
+ * Returns the first native node for a given LView, starting from the provided TNode.
+ *
+ * Native nodes are returned in the order in which those appear in the native tree (DOM).
+ */
+function getFirstNativeNode(lView: LView, tNode: TNode | null): RNode|null {
+ if (tNode !== null) {
+ ngDevMode && assertNodeOfPossibleTypes(
+ tNode, TNodeType.Element, TNodeType.Container, TNodeType.ElementContainer,
+ TNodeType.IcuContainer, TNodeType.Projection);
+
+ const tNodeType = tNode.type;
+
+ if (tNodeType === TNodeType.Element) {
+ return getNativeByTNode(tNode, lView);
+ } else if (tNodeType === TNodeType.Container) {
+ const lContainer = lView[tNode.index];
+ if (lContainer.length > CONTAINER_HEADER_OFFSET) {
+ const firstView = lContainer[CONTAINER_HEADER_OFFSET];
+ return getFirstNativeNode(firstView, firstView[TVIEW].firstChild);
+ } else {
+ return lContainer[NATIVE];
+ }
+ } else if (tNodeType === TNodeType.ElementContainer || tNodeType === TNodeType.IcuContainer) {
+ return getFirstNativeNode(lView, tNode.child);
+ } else {
+ const componentView = findComponentView(lView);
+ const componentHost = componentView[T_HOST] as TElementNode;
+ const parentView = getLViewParent(componentView);
+ const firstProjectedTNode: TNode|null =
+ (componentHost.projection as(TNode | null)[])[tNode.projection as number];
+
+ if (firstProjectedTNode != null) {
+ return getFirstNativeNode(parentView !, firstProjectedTNode);
+ } else {
+ return getFirstNativeNode(lView, tNode.next);
+ }
+ }
+ }
+
+ return null;
+}
+
export function getBeforeNodeForView(viewIndexInContainer: number, lContainer: LContainer): RNode|
null {
const nextViewIndex = CONTAINER_HEADER_OFFSET + viewIndexInContainer + 1;
if (nextViewIndex < lContainer.length) {
const lView = lContainer[nextViewIndex] as LView;
- ngDevMode && assertDefined(lView[T_HOST], 'Missing Host TNode');
- let tViewNodeChild = (lView[T_HOST] as TViewNode).child;
- if (tViewNodeChild !== null) {
- if (tViewNodeChild.type === TNodeType.ElementContainer ||
- tViewNodeChild.type === TNodeType.IcuContainer) {
- let currentChild = tViewNodeChild.child;
- while (currentChild && (currentChild.type === TNodeType.ElementContainer ||
- currentChild.type === TNodeType.IcuContainer)) {
- currentChild = currentChild.child;
- }
- tViewNodeChild = currentChild || tViewNodeChild;
- }
- return getNativeByTNodeOrNull(tViewNodeChild, lView);
- }
+ return getFirstNativeNode(lView, lView[TVIEW].firstChild);
}
return lContainer[NATIVE];
diff --git a/packages/core/test/acceptance/view_insertion_spec.ts b/packages/core/test/acceptance/view_insertion_spec.ts
index dd50834e7e..090401d357 100644
--- a/packages/core/test/acceptance/view_insertion_spec.ts
+++ b/packages/core/test/acceptance/view_insertion_spec.ts
@@ -10,6 +10,7 @@ import {CommonModule} from '@angular/common';
import {ChangeDetectorRef, Component, ComponentFactoryResolver, Directive, EmbeddedViewRef, Injector, NgModule, TemplateRef, ViewChild, ViewContainerRef, ViewRef} from '@angular/core';
import {TestBed} from '@angular/core/testing';
import {By} from '@angular/platform-browser';
+import {onlyInIvy} from '@angular/private/testing';
describe('view insertion', () => {
describe('of a simple template', () => {
@@ -347,6 +348,26 @@ describe('view insertion', () => {
});
+ onlyInIvy('VE incorrectly inserts views before ng-container content')
+ .it('should insert before a view with a ng-container where ViewContainerRef is injected',
+ () => {
+ expect(createAndInsertViews(`
+