diff --git a/packages/compiler/src/render3/r3_template_transform.ts b/packages/compiler/src/render3/r3_template_transform.ts
index 7b7e746add8..0567f0d0c71 100644
--- a/packages/compiler/src/render3/r3_template_transform.ts
+++ b/packages/compiler/src/render3/r3_template_transform.ts
@@ -217,9 +217,7 @@ class HtmlAstToIvyAst implements html.Visitor {
attrs.bound,
boundEvents,
directives,
- [
- /* no template attributes */
- ],
+ [/* no template attributes */],
children,
references,
variables,
@@ -560,6 +558,11 @@ class HtmlAstToIvyAst implements html.Visitor {
predicate: (blockName: string) => boolean,
): html.Block[] {
const relatedBlocks: html.Block[] = [];
+ // Whitespace-only text nodes to mark as processed only if a connected block follows them.
+ // We defer this so that significant whitespace (e.g. &ngsp; or , both of which look
+ // like insignificant whitespace by the time we get here) between two unrelated @if blocks
+ // is not silently eaten.
+ const pendingTextNodes: html.Text[] = [];
for (let i = primaryBlockIndex + 1; i < siblings.length; i++) {
const node = siblings[i];
@@ -569,11 +572,10 @@ class HtmlAstToIvyAst implements html.Visitor {
continue;
}
- // Ignore empty text nodes between blocks.
if (node instanceof html.Text && node.value.trim().length === 0) {
- // Add the text node to the processed nodes since we don't want
- // it to be generated between the connected nodes.
- this.processedNodes.add(node);
+ // Collect whitespace-only text nodes; only mark them as processed once we confirm
+ // they precede a connected block (e.g. @else / @else if).
+ pendingTextNodes.push(node);
continue;
}
@@ -582,6 +584,12 @@ class HtmlAstToIvyAst implements html.Visitor {
break;
}
+ // A connected block was found — commit the pending whitespace nodes as processed.
+ for (const pending of pendingTextNodes) {
+ this.processedNodes.add(pending);
+ }
+ pendingTextNodes.length = 0;
+
relatedBlocks.push(node);
this.processedNodes.add(node);
}
diff --git a/packages/compiler/test/render3/r3_template_transform_spec.ts b/packages/compiler/test/render3/r3_template_transform_spec.ts
index 59ac9b364e9..e92196a36f5 100644
--- a/packages/compiler/test/render3/r3_template_transform_spec.ts
+++ b/packages/compiler/test/render3/r3_template_transform_spec.ts
@@ -2636,6 +2636,58 @@ describe('R3 template transform', () => {
]);
});
+ it('should preserve &ngsp; between two sibling @if blocks', () => {
+ expectFromHtml(
+ `@if (true) {Hello}&ngsp;@if (true) {World}`,
+ ).toEqual([
+ ['IfBlock'],
+ ['IfBlockBranch', 'true'],
+ ['Element', 'span'],
+ ['Text', 'Hello'],
+ ['Text', ' '],
+ ['IfBlock'],
+ ['IfBlockBranch', 'true'],
+ ['Element', 'span'],
+ ['Text', 'World'],
+ ]);
+ });
+
+ it('should preserve plain whitespace between two sibling @if blocks when preserveWhitespaces is enabled', () => {
+ expectFromR3Nodes(
+ parse(`@if (true) {Hello}\n@if (true) {World}`, {
+ preserveWhitespaces: true,
+ }).nodes,
+ ).toEqual([
+ ['IfBlock'],
+ ['IfBlockBranch', 'true'],
+ ['Element', 'span'],
+ ['Text', 'Hello'],
+ ['Text', '\n'],
+ ['IfBlock'],
+ ['IfBlockBranch', 'true'],
+ ['Element', 'span'],
+ ['Text', 'World'],
+ ]);
+ });
+
+ it('should preserve between two sibling @if blocks when preserveWhitespaces is enabled', () => {
+ expectFromR3Nodes(
+ parse(`@if (true) {Hello} @if (true) {World}`, {
+ preserveWhitespaces: true,
+ }).nodes,
+ ).toEqual([
+ ['IfBlock'],
+ ['IfBlockBranch', 'true'],
+ ['Element', 'span'],
+ ['Text', 'Hello'],
+ ['Text', '\u00A0'],
+ ['IfBlock'],
+ ['IfBlockBranch', 'true'],
+ ['Element', 'span'],
+ ['Text', 'World'],
+ ]);
+ });
+
describe('validations', () => {
it('should report an if block without a condition', () => {
expect(() =>