diff --git a/packages/core/src/render3/i18n.ts b/packages/core/src/render3/i18n.ts
index bdf88e081e..20729712a2 100644
--- a/packages/core/src/render3/i18n.ts
+++ b/packages/core/src/render3/i18n.ts
@@ -19,7 +19,7 @@ import {attachI18nOpCodesDebug} from './instructions/lview_debug';
import {allocExpando, elementPropertyInternal, getOrCreateTNode, setInputsForProperty} from './instructions/shared';
import {LContainer, NATIVE} from './interfaces/container';
import {COMMENT_MARKER, ELEMENT_MARKER, I18nMutateOpCode, I18nMutateOpCodes, I18nUpdateOpCode, I18nUpdateOpCodes, IcuType, TI18n, TIcu} from './interfaces/i18n';
-import {TElementNode, TIcuContainerNode, TNode, TNodeType, TProjectionNode} from './interfaces/node';
+import {TElementNode, TIcuContainerNode, TNode, TNodeFlags, TNodeType, TProjectionNode} from './interfaces/node';
import {RComment, RElement, RText} from './interfaces/renderer';
import {SanitizerFn} from './interfaces/sanitization';
import {StylingContext} from './interfaces/styling';
@@ -910,6 +910,8 @@ function removeNode(index: number, viewData: LView) {
}
}
+ // Define this node as detached so that we don't risk projecting it
+ removedPhTNode.flags |= TNodeFlags.isDetached;
ngDevMode && ngDevMode.rendererRemoveNode++;
}
diff --git a/packages/core/src/render3/interfaces/node.ts b/packages/core/src/render3/interfaces/node.ts
index 4e88949712..3ecb11f346 100644
--- a/packages/core/src/render3/interfaces/node.ts
+++ b/packages/core/src/render3/interfaces/node.ts
@@ -47,19 +47,22 @@ export const enum TNodeType {
*/
export const enum TNodeFlags {
/** This bit is set if the node is a component */
- isComponent = 0b00001,
+ isComponent = 0b000001,
/** This bit is set if the node has been projected */
- isProjected = 0b00010,
+ isProjected = 0b000010,
/** This bit is set if any directive on this node has content queries */
- hasContentQuery = 0b00100,
+ hasContentQuery = 0b000100,
/** This bit is set if the node has any "class" inputs */
- hasClassInput = 0b01000,
+ hasClassInput = 0b001000,
/** This bit is set if the node has any "style" inputs */
- hasStyleInput = 0b10000,
+ hasStyleInput = 0b010000,
+
+ /** This bit is set if the node has been detached by i18n */
+ isDetached = 0b100000,
}
/**
diff --git a/packages/core/src/render3/node_manipulation.ts b/packages/core/src/render3/node_manipulation.ts
index 8fc447f32b..50dd8cd1d9 100644
--- a/packages/core/src/render3/node_manipulation.ts
+++ b/packages/core/src/render3/node_manipulation.ts
@@ -784,15 +784,17 @@ export function appendProjectedNodes(
appendChild(nodeToProject, tProjectionNode, lView);
} else {
while (nodeToProject) {
- if (nodeToProject.type === TNodeType.Projection) {
- appendProjectedNodes(
- lView, tProjectionNode, (nodeToProject as TProjectionNode).projection,
- findComponentView(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 (!(nodeToProject.flags & TNodeFlags.isDetached)) {
+ if (nodeToProject.type === TNodeType.Projection) {
+ appendProjectedNodes(
+ lView, tProjectionNode, (nodeToProject as TProjectionNode).projection,
+ findComponentView(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);
+ }
}
nodeToProject = nodeToProject.projectionNext;
}
diff --git a/packages/core/test/acceptance/i18n_spec.ts b/packages/core/test/acceptance/i18n_spec.ts
index 16c37d6a39..81e56ac043 100644
--- a/packages/core/test/acceptance/i18n_spec.ts
+++ b/packages/core/test/acceptance/i18n_spec.ts
@@ -939,8 +939,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
.toEqual('