diff --git a/packages/core/src/render3/i18n.ts b/packages/core/src/render3/i18n.ts index a76f72c7eb..2a4a1d3f6a 100644 --- a/packages/core/src/render3/i18n.ts +++ b/packages/core/src/render3/i18n.ts @@ -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; diff --git a/packages/core/src/render3/instructions/shared.ts b/packages/core/src/render3/instructions/shared.ts index b04fad12bc..d6601fbb9f 100644 --- a/packages/core/src/render3/instructions/shared.ts +++ b/packages/core/src/render3/instructions/shared.ts @@ -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. diff --git a/packages/core/test/acceptance/i18n_spec.ts b/packages/core/test/acceptance/i18n_spec.ts index e1a8b742ba..10eb551258 100644 --- a/packages/core/test/acceptance/i18n_spec.ts +++ b/packages/core/test/acceptance/i18n_spec.ts @@ -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 ', () => { + @Directive({selector: '[myDir]'}) + class Dir { + condition = true; + } + + @Component({ + selector: 'my-cmp', + template: ` +
+ Logged in +
+ + Not logged in + + `, + }) + 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', () => {