Skip to content

Commit 30efb6b

Browse files
ocombemhevery
authored andcommitted
fix(ivy): don't project removed placeholders with runtime i18n (angular#30783)
When translated content was projected, all of the content was reappended, even the placeholders that had been removed in the translation. To avoid that we added a new flag on `TNode` that specifies that a node is detached, in which case it should be ignored by the projection. FW-1319 #resolve PR Close angular#30783
1 parent 05a43ca commit 30efb6b

File tree

4 files changed

+23
-17
lines changed

4 files changed

+23
-17
lines changed

packages/core/src/render3/i18n.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import {attachI18nOpCodesDebug} from './instructions/lview_debug';
1919
import {allocExpando, elementPropertyInternal, getOrCreateTNode, setInputsForProperty} from './instructions/shared';
2020
import {LContainer, NATIVE} from './interfaces/container';
2121
import {COMMENT_MARKER, ELEMENT_MARKER, I18nMutateOpCode, I18nMutateOpCodes, I18nUpdateOpCode, I18nUpdateOpCodes, IcuType, TI18n, TIcu} from './interfaces/i18n';
22-
import {TElementNode, TIcuContainerNode, TNode, TNodeType, TProjectionNode} from './interfaces/node';
22+
import {TElementNode, TIcuContainerNode, TNode, TNodeFlags, TNodeType, TProjectionNode} from './interfaces/node';
2323
import {RComment, RElement, RText} from './interfaces/renderer';
2424
import {SanitizerFn} from './interfaces/sanitization';
2525
import {StylingContext} from './interfaces/styling';
@@ -910,6 +910,8 @@ function removeNode(index: number, viewData: LView) {
910910
}
911911
}
912912

913+
// Define this node as detached so that we don't risk projecting it
914+
removedPhTNode.flags |= TNodeFlags.isDetached;
913915
ngDevMode && ngDevMode.rendererRemoveNode++;
914916
}
915917

packages/core/src/render3/interfaces/node.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,19 +47,22 @@ export const enum TNodeType {
4747
*/
4848
export const enum TNodeFlags {
4949
/** This bit is set if the node is a component */
50-
isComponent = 0b00001,
50+
isComponent = 0b000001,
5151

5252
/** This bit is set if the node has been projected */
53-
isProjected = 0b00010,
53+
isProjected = 0b000010,
5454

5555
/** This bit is set if any directive on this node has content queries */
56-
hasContentQuery = 0b00100,
56+
hasContentQuery = 0b000100,
5757

5858
/** This bit is set if the node has any "class" inputs */
59-
hasClassInput = 0b01000,
59+
hasClassInput = 0b001000,
6060

6161
/** This bit is set if the node has any "style" inputs */
62-
hasStyleInput = 0b10000,
62+
hasStyleInput = 0b010000,
63+
64+
/** This bit is set if the node has been detached by i18n */
65+
isDetached = 0b100000,
6366
}
6467

6568
/**

packages/core/src/render3/node_manipulation.ts

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -784,15 +784,17 @@ export function appendProjectedNodes(
784784
appendChild(nodeToProject, tProjectionNode, lView);
785785
} else {
786786
while (nodeToProject) {
787-
if (nodeToProject.type === TNodeType.Projection) {
788-
appendProjectedNodes(
789-
lView, tProjectionNode, (nodeToProject as TProjectionNode).projection,
790-
findComponentView(projectedView));
791-
} else {
792-
// This flag must be set now or we won't know that this node is projected
793-
// if the nodes are inserted into a container later.
794-
nodeToProject.flags |= TNodeFlags.isProjected;
795-
appendProjectedNode(nodeToProject, tProjectionNode, lView, projectedView);
787+
if (!(nodeToProject.flags & TNodeFlags.isDetached)) {
788+
if (nodeToProject.type === TNodeType.Projection) {
789+
appendProjectedNodes(
790+
lView, tProjectionNode, (nodeToProject as TProjectionNode).projection,
791+
findComponentView(projectedView));
792+
} else {
793+
// This flag must be set now or we won't know that this node is projected
794+
// if the nodes are inserted into a container later.
795+
nodeToProject.flags |= TNodeFlags.isProjected;
796+
appendProjectedNode(nodeToProject, tProjectionNode, lView, projectedView);
797+
}
796798
}
797799
nodeToProject = nodeToProject.projectionNext;
798800
}

packages/core/test/acceptance/i18n_spec.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -939,8 +939,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
939939
.toEqual('<child><grand-child><div><b>Bonjour</b> monde!</div></grand-child></child>');
940940
});
941941

942-
// FW-1319 Runtime i18n should be able to remove projected placeholders
943-
xit('should be able to remove projected placeholders', () => {
942+
it('should be able to remove projected placeholders', () => {
944943
@Component({selector: 'grand-child', template: '<div><ng-content></ng-content></div>'})
945944
class GrandChild {
946945
}

0 commit comments

Comments
 (0)