fix(ivy): correctly project bare ICU expressions (#30696)

Projecting bare ICU expressions failed because we would assume that component's content nodes would be projected later and doing so at that point would be wasteful. But ICU nodes are handled independently and should be inserted immediately because they will be ignored by projections.

FW-1348 #resolve

PR Close #30696
This commit is contained in:
Olivier Combe 2019-06-03 15:05:34 +02:00 committed by Misko Hevery
parent 21328f2373
commit 680d38513b
3 changed files with 43 additions and 8 deletions

View File

@ -12,7 +12,7 @@
"master": {
"uncompressed": {
"runtime": 1440,
"main": 14487,
"main": 14664,
"polyfills": 43567
}
}

View File

@ -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<any>).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;
}
}

View File

@ -591,6 +591,33 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
fixture.detectChanges();
expect(fixture.nativeElement.innerHTML).toEqual('no email<!--ICU 2-->');
});
it('projection', () => {
@Component({selector: 'child', template: '<div><ng-content></ng-content></div>'})
class Child {
}
@Component({
selector: 'parent',
template: `
<child i18n>{
value // i18n(ph = "blah"),
plural,
=1 {one}
other {at least {{value}} .}
}</child>`
})
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', () => {