fix(ivy): correctly remove placeholders inside of *ngFor with runtime i18n (#29308)
Following my previous change for placeholders removal, some special code that was used to find the last created node was no longer needed and had wrong interactions with the *ngFor directive. Removing it fixed the issue. PR Close #29308
This commit is contained in:
parent
487d4157ac
commit
7c297e05f3
|
@ -626,16 +626,6 @@ export function i18nEnd(): void {
|
|||
i18nEndFirstPass(tView);
|
||||
}
|
||||
|
||||
function findLastNode(node: TNode): TNode {
|
||||
while (node.next) {
|
||||
node = node.next;
|
||||
}
|
||||
if (node.child) {
|
||||
return findLastNode(node.child);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* See `i18nEnd` above.
|
||||
*/
|
||||
|
@ -651,10 +641,8 @@ function i18nEndFirstPass(tView: TView) {
|
|||
|
||||
// Find the last node that was added before `i18nEnd`
|
||||
let lastCreatedNode = getPreviousOrParentTNode();
|
||||
if (lastCreatedNode.child) {
|
||||
lastCreatedNode = findLastNode(lastCreatedNode.child);
|
||||
}
|
||||
|
||||
// Read the instructions to insert/move/remove DOM elements
|
||||
const visitedNodes = readCreateOpCodes(rootIndex, tI18n.create, tI18n.icus, viewData);
|
||||
|
||||
// Remove deleted nodes
|
||||
|
|
|
@ -621,4 +621,20 @@ onlyInIvy('Ivy i18n logic').describe('i18n', function() {
|
|||
expect(fixture.nativeElement.innerHTML)
|
||||
.toBe('<div>Section 1</div><div>Section 2</div><div>Section 3</div>');
|
||||
});
|
||||
|
||||
it('should handle multiple i18n sections inside of *ngFor', () => {
|
||||
const template = `
|
||||
<ul *ngFor="let item of [1,2,3]">
|
||||
<li i18n>Section 1</li>
|
||||
<li i18n>Section 2</li>
|
||||
<li i18n>Section 3</li>
|
||||
</ul>
|
||||
`;
|
||||
const fixture = getFixtureWithOverrides({template});
|
||||
const element = fixture.nativeElement;
|
||||
for (let i = 0; i < element.children.length; i++) {
|
||||
const child = element.children[i];
|
||||
expect(child.innerHTML).toBe(`<li>Section 1</li><li>Section 2</li><li>Section 3</li>`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {NgForOfContext} from '@angular/common';
|
||||
import {noop} from '../../../compiler/src/render3/view/util';
|
||||
import {Component as _Component} from '../../src/core';
|
||||
import {defineComponent, defineDirective} from '../../src/render3/definition';
|
||||
|
@ -13,8 +14,8 @@ import {getTranslationForTemplate, i18n, i18nApply, i18nAttributes, i18nEnd, i18
|
|||
import {RenderFlags} from '../../src/render3/interfaces/definition';
|
||||
import {AttributeMarker} from '../../src/render3/interfaces/node';
|
||||
import {getNativeByIndex, getTNode} from '../../src/render3/util/view_utils';
|
||||
import {NgIf} from './common_with_def';
|
||||
import {allocHostVars, element, elementEnd, elementStart, template, text, nextContext, bind, elementProperty, projectionDef, projection, elementContainerStart, elementContainerEnd} from '../../src/render3/instructions';
|
||||
import {NgForOf, NgIf} from './common_with_def';
|
||||
import {allocHostVars, element, elementEnd, elementStart, template, text, nextContext, bind, elementProperty, projectionDef, projection, elementContainerStart, elementContainerEnd, textBinding} from '../../src/render3/instructions';
|
||||
import {COMMENT_MARKER, ELEMENT_MARKER, I18nMutateOpCode, I18nUpdateOpCode, I18nUpdateOpCodes, TI18n} from '../../src/render3/interfaces/i18n';
|
||||
import {HEADER_OFFSET, LView, TVIEW} from '../../src/render3/interfaces/view';
|
||||
import {ComponentFixture, TemplateFixture} from './render_util';
|
||||
|
@ -1334,6 +1335,70 @@ describe('Runtime i18n', () => {
|
|||
.toEqual(`<div><div>Section 1</div><div>Section 2</div><div>Section 3</div></div>`);
|
||||
});
|
||||
|
||||
it('should support multiple sibling i18n blocks inside of *ngFor', () => {
|
||||
// Translated template:
|
||||
// <ul *ngFor="let item of [1,2,3]">
|
||||
// <li i18n>Section 1</li>
|
||||
// <li i18n>Section 2</li>
|
||||
// <li i18n>Section 3</li>
|
||||
// </ul>
|
||||
|
||||
const MSG_DIV_1 = `Section 1`;
|
||||
const MSG_DIV_2 = `Section 2`;
|
||||
const MSG_DIV_3 = `Section 3`;
|
||||
|
||||
function liTemplate(rf: RenderFlags, ctx: NgForOfContext<string>) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
elementStart(0, 'ul');
|
||||
elementStart(1, 'li');
|
||||
{ i18n(2, MSG_DIV_1); }
|
||||
elementEnd();
|
||||
elementStart(3, 'li');
|
||||
{ i18n(4, MSG_DIV_2); }
|
||||
elementEnd();
|
||||
elementStart(5, 'li');
|
||||
{ i18n(6, MSG_DIV_3); }
|
||||
elementEnd();
|
||||
elementEnd();
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
i18nApply(2);
|
||||
i18nApply(4);
|
||||
i18nApply(6);
|
||||
}
|
||||
}
|
||||
|
||||
class MyApp {
|
||||
items: string[] = ['1', '2', '3'];
|
||||
|
||||
static ngComponentDef = defineComponent({
|
||||
type: MyApp,
|
||||
selectors: [['my-app']],
|
||||
factory: () => new MyApp(),
|
||||
consts: 2,
|
||||
vars: 1,
|
||||
template: (rf: RenderFlags, ctx: MyApp) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
elementStart(0, 'div');
|
||||
{
|
||||
template(1, liTemplate, 7, 0, 'ul', [AttributeMarker.Template, 'ngFor', 'ngForOf']);
|
||||
}
|
||||
elementEnd();
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(1, 'ngForOf', bind(ctx.items));
|
||||
}
|
||||
},
|
||||
directives: () => [NgForOf]
|
||||
});
|
||||
}
|
||||
|
||||
const fixture = new ComponentFixture(MyApp);
|
||||
expect(fixture.html)
|
||||
.toEqual(
|
||||
`<div><ul><li>Section 1</li><li>Section 2</li><li>Section 3</li></ul><ul><li>Section 1</li><li>Section 2</li><li>Section 3</li></ul><ul><li>Section 1</li><li>Section 2</li><li>Section 3</li></ul></div>`);
|
||||
});
|
||||
|
||||
it('should support attribute translations on removed elements', () => {
|
||||
// Translated template:
|
||||
// <div i18n i18n-title title="start {{exp2}} middle {{exp1}} end">
|
||||
|
|
Loading…
Reference in New Issue