diff --git a/integration/_payload-limits.json b/integration/_payload-limits.json index cc45acecbb..35c68b565d 100644 --- a/integration/_payload-limits.json +++ b/integration/_payload-limits.json @@ -12,7 +12,7 @@ "master": { "uncompressed": { "runtime": 1440, - "main": 14487, + "main": 14664, "polyfills": 43567 } } diff --git a/packages/core/src/render3/node_manipulation.ts b/packages/core/src/render3/node_manipulation.ts index 8a586c5fd7..8fc447f32b 100644 --- a/packages/core/src/render3/node_manipulation.ts +++ b/packages/core/src/render3/node_manipulation.ts @@ -15,7 +15,7 @@ import {ComponentDef} from './interfaces/definition'; import {NodeInjectorFactory} from './interfaces/injector'; import {TElementNode, TNode, TNodeFlags, TNodeType, TProjectionNode, TViewNode, unusedValueExportToPlacateAjd as unused2} from './interfaces/node'; import {unusedValueExportToPlacateAjd as unused3} from './interfaces/projection'; -import {ProceduralRenderer3, RComment, RElement, RNode, RText, Renderer3, isProceduralRenderer, unusedValueExportToPlacateAjd as unused4} from './interfaces/renderer'; +import {ProceduralRenderer3, RElement, RNode, RText, Renderer3, isProceduralRenderer, unusedValueExportToPlacateAjd as unused4} from './interfaces/renderer'; import {CHILD_HEAD, CLEANUP, FLAGS, HookData, LView, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, TVIEW, T_HOST, unusedValueExportToPlacateAjd as unused5} from './interfaces/view'; import {assertNodeType} from './node_assert'; import {renderStringify} from './util/misc_utils'; @@ -558,11 +558,12 @@ function getRenderParent(tNode: TNode, currentView: LView): RElement|null { // Skip over element and ICU containers as those are represented by a comment node and // can't be used as a render parent. - const parent = getHighestElementOrICUContainer(tNode).parent; + const parent = getHighestElementOrICUContainer(tNode); + const renderParent = parent.parent; // If the parent is null, then we are inserting across views: either into an embedded view or a // component view. - if (parent == null) { + if (renderParent == null) { const hostTNode = currentView[T_HOST] !; if (hostTNode.type === TNodeType.View) { // We are inserting a root element of an embedded view We might delay insertion of children @@ -579,10 +580,17 @@ function getRenderParent(tNode: TNode, currentView: LView): RElement|null { return getHostNative(currentView); } } else { - ngDevMode && assertNodeType(parent, TNodeType.Element); - if (parent.flags & TNodeFlags.isComponent) { + const isIcuCase = parent && parent.type === TNodeType.IcuContainer; + // If the parent of this node is an ICU container, then it is represented by comment node and we + // need to use it as an anchor. If it is projected then its direct parent node is the renderer. + if (isIcuCase && parent.flags & TNodeFlags.isProjected) { + return getNativeByTNode(parent, currentView).parentNode as RElement; + } + + ngDevMode && assertNodeType(renderParent, TNodeType.Element); + if (renderParent.flags & TNodeFlags.isComponent && !isIcuCase) { const tData = currentView[TVIEW].data; - const tNode = tData[parent.index] as TNode; + const tNode = tData[renderParent.index] as TNode; const encapsulation = (tData[tNode.directiveStart] as ComponentDef).encapsulation; // We've got a parent which is an element in the current view. We just need to verify if the @@ -597,7 +605,7 @@ function getRenderParent(tNode: TNode, currentView: LView): RElement|null { } } - return getNativeByTNode(parent, currentView) as RElement; + return getNativeByTNode(renderParent, currentView) as RElement; } } diff --git a/packages/core/test/acceptance/i18n_spec.ts b/packages/core/test/acceptance/i18n_spec.ts index ad3b47fc97..a71d17304c 100644 --- a/packages/core/test/acceptance/i18n_spec.ts +++ b/packages/core/test/acceptance/i18n_spec.ts @@ -591,6 +591,33 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { fixture.detectChanges(); expect(fixture.nativeElement.innerHTML).toEqual('no email'); }); + + it('projection', () => { + @Component({selector: 'child', template: '
'}) + class Child { + } + + @Component({ + selector: 'parent', + template: ` + { + value // i18n(ph = "blah"), + plural, + =1 {one} + other {at least {{value}} .} + }` + }) + class Parent { + value = 3; + } + TestBed.configureTestingModule({declarations: [Parent, Child]}); + ɵi18nConfigureLocalize({translations: {}}); + + const fixture = TestBed.createComponent(Parent); + fixture.detectChanges(); + + expect(fixture.nativeElement.innerHTML).toContain('at least'); + }); }); describe('should support attributes', () => {