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;
|
||||
}
|
||||
|
||||
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++;
|
||||
const nextNode = tNode.next;
|
||||
const viewData = getLView();
|
||||
if (!previousTNode) {
|
||||
previousTNode = parentTNode;
|
||||
}
|
||||
|
@ -737,7 +737,7 @@ function readCreateOpCodes(
|
|||
assertDefined(
|
||||
currentTNode !,
|
||||
`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;
|
||||
case I18nMutateOpCode.Select:
|
||||
const nodeIndex = opCode >>> I18nMutateOpCode.SHIFT_REF;
|
||||
|
|
|
@ -277,7 +277,9 @@ function createTNodeAtIndex(
|
|||
const tParentNode = parentInSameView ? parent as TElementNode | TContainerNode : null;
|
||||
const tNode = tView.data[adjustedIndex] =
|
||||
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;
|
||||
}
|
||||
// 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 {setDelayProjection} from '@angular/core/src/render3/instructions/projection';
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
import {By} from '@angular/platform-browser';
|
||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||
import {onlyInIvy} from '@angular/private/testing';
|
||||
|
||||
|
@ -372,6 +373,38 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
|
|||
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', () => {
|
||||
|
|
Loading…
Reference in New Issue