fix(ivy): handle ICU expressions in `executeActionOnNode` (#31313)

When `walkTNodeTree` was refactored, the case of ICU expressions was forgotten (because it was handled in the `else` previously).
This PR fixes that to handle it like `ElementContainer`.

FW-1411 #resolve
PR Close #31313
This commit is contained in:
Olivier Combe 2019-06-27 16:10:35 +02:00 committed by Alex Rickabaugh
parent 119004c7d4
commit 4f38419e33
3 changed files with 49 additions and 13 deletions

View File

@ -14,7 +14,7 @@ import {attachPatchData} from './context_discovery';
import {CONTAINER_HEADER_OFFSET, LContainer, NATIVE, unusedValueExportToPlacateAjd as unused1} from './interfaces/container';
import {ComponentDef} from './interfaces/definition';
import {NodeInjectorFactory} from './interfaces/injector';
import {TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeType, TProjectionNode, TViewNode, unusedValueExportToPlacateAjd as unused2} from './interfaces/node';
import {TElementContainerNode, TElementNode, TIcuContainerNode, TNode, TNodeFlags, TNodeType, TProjectionNode, TViewNode, unusedValueExportToPlacateAjd as unused2} from './interfaces/node';
import {unusedValueExportToPlacateAjd as unused3} from './interfaces/projection';
import {ProceduralRenderer3, RElement, RNode, RText, Renderer3, isProceduralRenderer, unusedValueExportToPlacateAjd as unused4} from './interfaces/renderer';
import {StylingContext} from './interfaces/styling';
@ -844,23 +844,23 @@ function executeActionOnContainer(
/**
* `executeActionOnElementContainer` performs an operation on the ng-container node and its child
* nodes as specified by the `action` (insert, detach, destroy).
* `executeActionOnElementContainerOrIcuContainer` performs an operation on the ng-container node
* and its child nodes as specified by the `action` (insert, detach, destroy).
*
* @param renderer Renderer to use
* @param action action to perform (insert, detach, destroy)
* @param lView The LView which needs to be inserted, detached, destroyed.
* @param tElementContainerNode The TNode associated with the ElementContainer.
* @param tNode The TNode associated with the `ElementContainer` or `IcuContainer`.
* @param renderParent parent DOM element for insertion/removal.
* @param beforeNode Before which node the insertions should happen.
*/
function executeActionOnElementContainer(
function executeActionOnElementContainerOrIcuContainer(
renderer: Renderer3, action: WalkTNodeTreeAction, lView: LView,
tElementContainerNode: TElementContainerNode, renderParent: RElement | null,
tNode: TElementContainerNode | TIcuContainerNode, renderParent: RElement | null,
beforeNode: RNode | null | undefined) {
const node = lView[tElementContainerNode.index];
const node = lView[tNode.index];
executeActionOnElementOrContainer(action, renderer, renderParent, node, beforeNode);
let childTNode: TNode|null = tElementContainerNode.child;
let childTNode: TNode|null = tNode.child;
while (childTNode) {
executeActionOnNode(renderer, action, lView, childTNode, renderParent, beforeNode);
childTNode = childTNode.next;
@ -871,9 +871,11 @@ function executeActionOnNode(
renderer: Renderer3, action: WalkTNodeTreeAction, lView: LView, tNode: TNode,
renderParent: RElement | null, beforeNode: RNode | null | undefined): void {
const elementContainerRootTNodeType = tNode.type;
if (elementContainerRootTNodeType === TNodeType.ElementContainer) {
executeActionOnElementContainer(
renderer, action, lView, tNode as TElementContainerNode, renderParent, beforeNode);
if (elementContainerRootTNodeType === TNodeType.ElementContainer ||
elementContainerRootTNodeType === TNodeType.IcuContainer) {
executeActionOnElementContainerOrIcuContainer(
renderer, action, lView, tNode as TElementContainerNode | TIcuContainerNode, renderParent,
beforeNode);
} else if (elementContainerRootTNodeType === TNodeType.Projection) {
executeActionOnProjection(
renderer, action, lView, tNode as TProjectionNode, renderParent, beforeNode);

View File

@ -772,6 +772,40 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
expect(fixture.debugElement.nativeElement.innerHTML)
.toBe('<my-cmp><div>ONE<!--ICU 15--></div><!--container--></my-cmp>');
});
it('with nested containers', () => {
@Component({
selector: 'comp',
template: `
<ng-container [ngSwitch]="visible">
<ng-container *ngSwitchCase="isVisible()" i18n>
{type, select, A { A } B { B } other { C }}
</ng-container>
<ng-container *ngSwitchCase="!isVisible()" i18n>
{type, select, A1 { A1 } B1 { B1 } other { C1 }}
</ng-container>
</ng-container>
`,
})
class Comp {
type = 'A';
visible = true;
isVisible() { return true; }
}
TestBed.configureTestingModule({declarations: [Comp]});
const fixture = TestBed.createComponent(Comp);
fixture.detectChanges();
expect(fixture.debugElement.nativeElement.innerHTML).toContain('A');
fixture.componentInstance.visible = false;
fixture.detectChanges();
expect(fixture.debugElement.nativeElement.innerHTML).not.toContain('A');
expect(fixture.debugElement.nativeElement.innerHTML).toContain('C1');
});
});
describe('should support attributes', () => {

View File

@ -675,10 +675,10 @@
"name": "executeActionOnContainer"
},
{
"name": "executeActionOnElementContainer"
"name": "executeActionOnElementOrContainer"
},
{
"name": "executeActionOnElementOrContainer"
"name": "executeActionOnElementContainerOrIcuContainer"
},
{
"name": "executeActionOnNode"