Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(ivy): node placed in incorrect order inside ngFor with ng-container #32324

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
20 changes: 15 additions & 5 deletions packages/core/src/render3/node_manipulation.ts
Expand Up @@ -669,12 +669,22 @@ export function getBeforeNodeForView(viewIndexInContainer: number, lContainer: L
if (nextViewIndex < lContainer.length) {
const lView = lContainer[nextViewIndex] as LView;
ngDevMode && assertDefined(lView[T_HOST], 'Missing Host TNode');
const tViewNodeChild = (lView[T_HOST] as TViewNode).child;
return tViewNodeChild !== null ? getNativeByTNodeOrNull(tViewNodeChild, lView) :
lContainer[NATIVE];
} else {
return lContainer[NATIVE];
let tViewNodeChild = (lView[T_HOST] as TViewNode).child;
if (tViewNodeChild !== null) {
if (tViewNodeChild.type === TNodeType.ElementContainer ||
tViewNodeChild.type === TNodeType.IcuContainer) {
let currentChild = tViewNodeChild.child;
while (currentChild && (currentChild.type === TNodeType.ElementContainer ||
currentChild.type === TNodeType.IcuContainer)) {
currentChild = currentChild.child;
}
tViewNodeChild = currentChild || tViewNodeChild;
}
return getNativeByTNodeOrNull(tViewNodeChild, lView);
}
}

return lContainer[NATIVE];
}

/**
Expand Down
172 changes: 172 additions & 0 deletions packages/core/test/acceptance/view_container_ref_spec.ts
Expand Up @@ -1276,6 +1276,178 @@ describe('ViewContainerRef', () => {
'</loop-comp>');
});

it('should insert elements in the proper order when template root is an ng-container', () => {
@Component({
template: `
<ng-container *ngFor="let item of items">|{{ item }}|</ng-container>
crisbeto marked this conversation as resolved.
Show resolved Hide resolved
`
})
class App {
items = ['one', 'two', 'three'];
}

TestBed.configureTestingModule({imports: [CommonModule], declarations: [App]});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();

expect(fixture.nativeElement.textContent).toBe('|one||two||three|');

fixture.componentInstance.items.unshift('zero');
fixture.detectChanges();

expect(fixture.nativeElement.textContent).toBe('|zero||one||two||three|');

fixture.componentInstance.items.push('four');
fixture.detectChanges();

expect(fixture.nativeElement.textContent).toBe('|zero||one||two||three||four|');

fixture.componentInstance.items.splice(3, 0, 'two point five');
fixture.detectChanges();

expect(fixture.nativeElement.textContent)
.toBe('|zero||one||two||two point five||three||four|');
});

it('should insert elements in the proper order when template root is an ng-container and is wrapped by an ng-container',
() => {
@Component({
template: `
<ng-container>
<ng-container *ngFor="let item of items">|{{ item }}|</ng-container>
</ng-container>
`
})
class App {
items = ['one', 'two', 'three'];
}

TestBed.configureTestingModule({imports: [CommonModule], declarations: [App]});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();

expect(fixture.nativeElement.textContent).toBe('|one||two||three|');

fixture.componentInstance.items.unshift('zero');
fixture.detectChanges();

expect(fixture.nativeElement.textContent).toBe('|zero||one||two||three|');

fixture.componentInstance.items.push('four');
fixture.detectChanges();

expect(fixture.nativeElement.textContent).toBe('|zero||one||two||three||four|');

fixture.componentInstance.items.splice(3, 0, 'two point five');
fixture.detectChanges();

expect(fixture.nativeElement.textContent)
.toBe('|zero||one||two||two point five||three||four|');
});

it('should insert elements in the proper order when template root is an ng-container and first node is a ng-container',
() => {
@Component({
template: `
<ng-container *ngFor="let item of items"><ng-container>|{{ item }}|</ng-container></ng-container>
`
})
class App {
items = ['one', 'two', 'three'];
}

TestBed.configureTestingModule({imports: [CommonModule], declarations: [App]});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();

expect(fixture.nativeElement.textContent).toBe('|one||two||three|');

fixture.componentInstance.items.unshift('zero');
fixture.detectChanges();

expect(fixture.nativeElement.textContent).toBe('|zero||one||two||three|');

fixture.componentInstance.items.push('four');
fixture.detectChanges();

expect(fixture.nativeElement.textContent).toBe('|zero||one||two||three||four|');

fixture.componentInstance.items.splice(3, 0, 'two point five');
fixture.detectChanges();

expect(fixture.nativeElement.textContent)
.toBe('|zero||one||two||two point five||three||four|');
});

it('should insert elements in the proper order when template root is an ng-container, wrapped in an ng-container with the root node as an ng-container',
() => {
@Component({
template: `
<ng-container>
<ng-container *ngFor="let item of items"><ng-container>|{{ item }}|</ng-container></ng-container>
</ng-container>
`
})
class App {
items = ['one', 'two', 'three'];
}

TestBed.configureTestingModule({imports: [CommonModule], declarations: [App]});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();

expect(fixture.nativeElement.textContent).toBe('|one||two||three|');

fixture.componentInstance.items.unshift('zero');
fixture.detectChanges();

expect(fixture.nativeElement.textContent).toBe('|zero||one||two||three|');

fixture.componentInstance.items.push('four');
fixture.detectChanges();

expect(fixture.nativeElement.textContent).toBe('|zero||one||two||three||four|');

fixture.componentInstance.items.splice(3, 0, 'two point five');
fixture.detectChanges();

expect(fixture.nativeElement.textContent)
.toBe('|zero||one||two||two point five||three||four|');
});

it('should insert elements in the proper order when the first child node is an ICU expression',
() => {
@Component({
template: `
<ng-container *ngFor="let item of items">{count, select, other {|{{ item }}|}}</ng-container>
`
})
class App {
items = ['one', 'two', 'three'];
}

TestBed.configureTestingModule({imports: [CommonModule], declarations: [App]});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();

expect(fixture.nativeElement.textContent).toBe('|one||two||three|');

fixture.componentInstance.items.unshift('zero');
fixture.detectChanges();

expect(fixture.nativeElement.textContent).toBe('|zero||one||two||three|');

fixture.componentInstance.items.push('four');
fixture.detectChanges();

expect(fixture.nativeElement.textContent).toBe('|zero||one||two||three||four|');

fixture.componentInstance.items.splice(3, 0, 'two point five');
fixture.detectChanges();

expect(fixture.nativeElement.textContent)
.toBe('|zero||one||two||two point five||three||four|');
});
});

describe('lifecycle hooks', () => {
Expand Down