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:
Olivier Combe 2019-03-14 16:26:01 +01:00 committed by Matias Niemelä
parent 487d4157ac
commit 7c297e05f3
3 changed files with 84 additions and 15 deletions

View File

@ -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

View File

@ -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>`);
}
});
});

View File

@ -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">