fix(ivy): correctly set `TView.firstChild` with runtime i18n (#30920)
`TView.firstChild` was defined as the first node with index 0, but when we use runtime i18n then the instruction `i18nStart` can be the one with index 0 and the first node will be index 1 or more. With this fix we set `TView.firstChild` when we see the first node with index 0 or later if `TView.firstChild` is still null. FW-1367 #resolve PR Close #30920
This commit is contained in:
parent
2b9a4cc6a6
commit
4155ed439a
|
@ -488,10 +488,10 @@ function i18nStartFirstPass(
|
||||||
tView.data[index + HEADER_OFFSET] = tI18n;
|
tView.data[index + HEADER_OFFSET] = tI18n;
|
||||||
}
|
}
|
||||||
|
|
||||||
function appendI18nNode(tNode: TNode, parentTNode: TNode, previousTNode: TNode | null): TNode {
|
function appendI18nNode(
|
||||||
|
tNode: TNode, parentTNode: TNode, previousTNode: TNode | null, viewData: LView): TNode {
|
||||||
ngDevMode && ngDevMode.rendererMoveNode++;
|
ngDevMode && ngDevMode.rendererMoveNode++;
|
||||||
const nextNode = tNode.next;
|
const nextNode = tNode.next;
|
||||||
const viewData = getLView();
|
|
||||||
if (!previousTNode) {
|
if (!previousTNode) {
|
||||||
previousTNode = parentTNode;
|
previousTNode = parentTNode;
|
||||||
}
|
}
|
||||||
|
@ -737,7 +737,7 @@ function readCreateOpCodes(
|
||||||
assertDefined(
|
assertDefined(
|
||||||
currentTNode !,
|
currentTNode !,
|
||||||
`You need to create or select a node before you can insert it into the DOM`);
|
`You need to create or select a node before you can insert it into the DOM`);
|
||||||
previousTNode = appendI18nNode(currentTNode !, destinationTNode, previousTNode);
|
previousTNode = appendI18nNode(currentTNode !, destinationTNode, previousTNode, viewData);
|
||||||
break;
|
break;
|
||||||
case I18nMutateOpCode.Select:
|
case I18nMutateOpCode.Select:
|
||||||
const nodeIndex = opCode >>> I18nMutateOpCode.SHIFT_REF;
|
const nodeIndex = opCode >>> I18nMutateOpCode.SHIFT_REF;
|
||||||
|
|
|
@ -277,7 +277,9 @@ function createTNodeAtIndex(
|
||||||
const tParentNode = parentInSameView ? parent as TElementNode | TContainerNode : null;
|
const tParentNode = parentInSameView ? parent as TElementNode | TContainerNode : null;
|
||||||
const tNode = tView.data[adjustedIndex] =
|
const tNode = tView.data[adjustedIndex] =
|
||||||
createTNode(tParentNode, type, adjustedIndex, name, attrs);
|
createTNode(tParentNode, type, adjustedIndex, name, attrs);
|
||||||
if (index === 0) {
|
// The first node is not always the one at index 0, in case of i18n, index 0 can be the
|
||||||
|
// instruction `i18nStart` and the first node has the index 1 or more
|
||||||
|
if (index === 0 || !tView.firstChild) {
|
||||||
tView.firstChild = tNode;
|
tView.firstChild = tNode;
|
||||||
}
|
}
|
||||||
// Now link ourselves into the tree.
|
// Now link ourselves into the tree.
|
||||||
|
|
|
@ -11,6 +11,7 @@ import localeRo from '@angular/common/locales/ro';
|
||||||
import {Component, ContentChild, ContentChildren, Directive, HostBinding, Input, LOCALE_ID, QueryList, TemplateRef, Type, ViewChild, ViewContainerRef, ɵi18nConfigureLocalize} from '@angular/core';
|
import {Component, ContentChild, ContentChildren, Directive, HostBinding, Input, LOCALE_ID, QueryList, TemplateRef, Type, ViewChild, ViewContainerRef, ɵi18nConfigureLocalize} from '@angular/core';
|
||||||
import {setDelayProjection} from '@angular/core/src/render3/instructions/projection';
|
import {setDelayProjection} from '@angular/core/src/render3/instructions/projection';
|
||||||
import {TestBed} from '@angular/core/testing';
|
import {TestBed} from '@angular/core/testing';
|
||||||
|
import {By} from '@angular/platform-browser';
|
||||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||||
import {onlyInIvy} from '@angular/private/testing';
|
import {onlyInIvy} from '@angular/private/testing';
|
||||||
|
|
||||||
|
@ -372,6 +373,38 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
|
||||||
expect(child).toHaveText('Mon logo');
|
expect(child).toHaveText('Mon logo');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should correctly find context for an element inside i18n section in <ng-template>', () => {
|
||||||
|
@Directive({selector: '[myDir]'})
|
||||||
|
class Dir {
|
||||||
|
condition = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-cmp',
|
||||||
|
template: `
|
||||||
|
<div *ngIf="isLogged; else notLoggedIn">
|
||||||
|
<span>Logged in</span>
|
||||||
|
</div>
|
||||||
|
<ng-template #notLoggedIn i18n>
|
||||||
|
<a myDir>Not logged in</a>
|
||||||
|
</ng-template>
|
||||||
|
`,
|
||||||
|
})
|
||||||
|
class Cmp {
|
||||||
|
isLogged = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [Cmp, Dir],
|
||||||
|
});
|
||||||
|
const fixture = TestBed.createComponent(Cmp);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const a = fixture.debugElement.query(By.css('a'));
|
||||||
|
const dir = a.injector.get(Dir);
|
||||||
|
expect(dir.condition).toEqual(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('should support ICU expressions', () => {
|
describe('should support ICU expressions', () => {
|
||||||
|
|
Loading…
Reference in New Issue