Skip to content

Commit

Permalink
fix(ivy): handle elements with local refs in i18n blocks (#33415)
Browse files Browse the repository at this point in the history
Prior to this commit, i18n logic which ensures that elements removed in a translation are also removed in DOM, didn't take into account the fact that elements may have local refs. As a result, remove operation failed, since there is no corresponding tNode found. This commit updates the logic to skip all local refs while going though the list of nodes to ensure that DOM matches elements present in translation.

PR Close #33415
  • Loading branch information
AndrewKushnir committed Oct 29, 2019
1 parent da4eb91 commit bd40c89
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 3 deletions.
17 changes: 14 additions & 3 deletions packages/core/src/render3/i18n.ts
Expand Up @@ -683,10 +683,21 @@ function i18nEndFirstPass(lView: LView, tView: TView) {
const visitedNodes = readCreateOpCodes(rootIndex, tI18n.create, lView);

// Remove deleted nodes
for (let i = rootIndex + 1; i <= lastCreatedNode.index - HEADER_OFFSET; i++) {
if (visitedNodes.indexOf(i) === -1) {
removeNode(i, lView, /* markAsDetached */ true);
let index = rootIndex + 1;
while (index <= lastCreatedNode.index - HEADER_OFFSET) {
if (visitedNodes.indexOf(index) === -1) {
removeNode(index, lView, /* markAsDetached */ true);
}
// Check if an element has any local refs and skip them
const tNode = getTNode(index, lView);
if (tNode && (tNode.type === TNodeType.Element || tNode.type === TNodeType.ElementContainer) &&
tNode.localNames !== null) {
// Divide by 2 to get the number of local refs,
// since they are stored as an array that also includes directive indexes,
// i.e. ["localRef", directiveIndex, ...]
index += tNode.localNames.length >> 1;
}
index++;
}
}

Expand Down
37 changes: 37 additions & 0 deletions packages/core/test/acceptance/i18n_spec.ts
Expand Up @@ -287,6 +287,43 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
expect(instance.clicks).toBe(1);
});

it('should support local refs inside i18n block', () => {
loadTranslations({
[computeMsgId(
'{$START_TAG_NG_CONTAINER} One {$CLOSE_TAG_NG_CONTAINER}' +
'{$START_TAG_DIV} Two {$CLOSE_TAG_DIV}' +
'{$START_TAG_SPAN} Three {$CLOSE_TAG_SPAN}')]:
'{$START_TAG_NG_CONTAINER} Une {$CLOSE_TAG_NG_CONTAINER}' +
'{$START_TAG_DIV} Deux {$CLOSE_TAG_DIV}' +
'{$START_TAG_SPAN} Trois {$CLOSE_TAG_SPAN}'
});
const fixture = initWithTemplate(AppComp, `
<div i18n>
<ng-container #localRefA> One </ng-container>
<div #localRefB> Two </div>
<span #localRefC> Three </span>
</div>
`);
expect(fixture.nativeElement.textContent).toBe(' Une Deux Trois ');
});

it('should handle local refs correctly in case an element is removed in translation', () => {
loadTranslations({
[computeMsgId(
'{$START_TAG_NG_CONTAINER} One {$CLOSE_TAG_NG_CONTAINER}' +
'{$START_TAG_DIV} Two {$CLOSE_TAG_DIV}' +
'{$START_TAG_SPAN} Three {$CLOSE_TAG_SPAN}')]: '{$START_TAG_DIV} Deux {$CLOSE_TAG_DIV}'
});
const fixture = initWithTemplate(AppComp, `
<div i18n>
<ng-container #localRefA> One </ng-container>
<div #localRefB> Two </div>
<span #localRefC> Three </span>
</div>
`);
expect(fixture.nativeElement.textContent).toBe(' Deux ');
});

describe('ng-container and ng-template support', () => {
it('should support ng-container', () => {
loadTranslations({[computeMsgId('text')]: 'texte'});
Expand Down

0 comments on commit bd40c89

Please sign in to comment.