diff --git a/packages/core/src/render3/instructions.ts b/packages/core/src/render3/instructions.ts index 3a9d315d0e8..705cded3df4 100644 --- a/packages/core/src/render3/instructions.ts +++ b/packages/core/src/render3/instructions.ts @@ -706,7 +706,7 @@ export function element( * @param attrs Set of attributes to be used when matching directives. * @param localRefs A set of local reference bindings on the element. * - * Even if this instruction accepts a set of attributes no actual attribute values are propoagted to + * Even if this instruction accepts a set of attributes no actual attribute values are propagated to * the DOM (as a comment node can't have attributes). Attributes are here only for directive * matching purposes and setting initial inputs of directives. */ diff --git a/packages/core/src/render3/node_manipulation.ts b/packages/core/src/render3/node_manipulation.ts index 24234f14255..17f28171f02 100644 --- a/packages/core/src/render3/node_manipulation.ts +++ b/packages/core/src/render3/node_manipulation.ts @@ -56,6 +56,15 @@ export function getParentLNode(node: LNode): LElementNode|LElementContainerNode| return readElementValue(parent ? node.view[parent.index] : node.view[HOST_NODE]); } +/** + * Retrieves render parent LElementNode for a given view. + * Might be null if a view is not yet attatched to any container. + */ +function getRenderParent(viewNode: LViewNode): LElementNode|null { + const container = getParentLNode(viewNode); + return container ? container.data[RENDER_PARENT] : null; +} + const enum WalkLNodeTreeAction { /** node insert in the native environment */ Insert = 0, @@ -565,6 +574,20 @@ export function canInsertNativeNode(parent: LNode, currentView: LViewData): bool } } +/** + * Inserts a native node before another native node for a given parent using {@link Renderer3}. + * This is a utility function that can be used when native nodes were determined - it abstracts an + * actual renderer being used. + */ +function nativeInsertBefore( + renderer: Renderer3, parent: RElement, child: RNode, beforeNode: RNode | null): void { + if (isProceduralRenderer(renderer)) { + renderer.insertBefore(parent, child, beforeNode); + } else { + parent.insertBefore(child, beforeNode, true); + } +} + /** * Appends the `child` element to the `parent`. * @@ -585,15 +608,16 @@ export function appendChild(parent: LNode, child: RNode | null, currentView: LVi const index = views.indexOf(parent as LViewNode); const beforeNode = index + 1 < views.length ? (getChildLNode(views[index + 1]) !).native : container.native; - isProceduralRenderer(renderer) ? - renderer.insertBefore(renderParent !.native, child, beforeNode) : - renderParent !.native.insertBefore(child, beforeNode, true); + nativeInsertBefore(renderer, renderParent !.native, child, beforeNode); } else if (parent.tNode.type === TNodeType.ElementContainer) { const beforeNode = parent.native; - const renderParent = getParentLNode(parent) as LElementNode; - isProceduralRenderer(renderer) ? - renderer.insertBefore(renderParent !.native, child, beforeNode) : - renderParent !.native.insertBefore(child, beforeNode, true); + const grandParent = getParentLNode(parent) as LElementNode | LViewNode; + if (grandParent.tNode.type === TNodeType.View) { + const renderParent = getRenderParent(grandParent as LViewNode); + nativeInsertBefore(renderer, renderParent !.native, child, beforeNode); + } else { + nativeInsertBefore(renderer, (grandParent as LElementNode).native, child, beforeNode); + } } else { isProceduralRenderer(renderer) ? renderer.appendChild(parent.native !as RElement, child) : parent.native !.appendChild(child); 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 d4d1870f4cb..0188508d67d 100644 --- a/packages/core/test/bundling/hello_world/bundle.golden_symbols.json +++ b/packages/core/test/bundling/hello_world/bundle.golden_symbols.json @@ -176,6 +176,9 @@ { "name": "getRenderFlags" }, + { + "name": "getRenderParent" + }, { "name": "getRootView" }, @@ -200,6 +203,9 @@ { "name": "namespaceHTML" }, + { + "name": "nativeInsertBefore" + }, { "name": "readElementValue" }, diff --git a/packages/core/test/bundling/todo/bundle.golden_symbols.json b/packages/core/test/bundling/todo/bundle.golden_symbols.json index 2f713715326..757637d08bf 100644 --- a/packages/core/test/bundling/todo/bundle.golden_symbols.json +++ b/packages/core/test/bundling/todo/bundle.golden_symbols.json @@ -590,6 +590,9 @@ { "name": "getRenderFlags" }, + { + "name": "getRenderParent" + }, { "name": "getRenderer" }, @@ -728,6 +731,9 @@ { "name": "namespaceHTML" }, + { + "name": "nativeInsertBefore" + }, { "name": "nextContext" }, diff --git a/packages/core/test/render3/integration_spec.ts b/packages/core/test/render3/integration_spec.ts index 7d2669b3238..66e9ce7dcd7 100644 --- a/packages/core/test/render3/integration_spec.ts +++ b/packages/core/test/render3/integration_spec.ts @@ -16,7 +16,8 @@ import {HEADER_OFFSET} from '../../src/render3/interfaces/view'; import {sanitizeUrl} from '../../src/sanitization/sanitization'; import {Sanitizer, SecurityContext} from '../../src/sanitization/security'; -import {ComponentFixture, TemplateFixture, containerEl, renderToHtml} from './render_util'; +import {NgIf} from './common_with_def'; +import {ComponentFixture, TemplateFixture, containerEl, createComponent, renderToHtml} from './render_util'; describe('render3 integration test', () => { @@ -445,6 +446,154 @@ describe('render3 integration test', () => { expect(fixture.html).toEqual('