Skip to content

Commit e2b1e15

Browse files
committed
fix(core): don’t detach nested view containers when destroying a view
When a view is destroyed, we destroy all views in view containers and should not detach them. However, previously, we also detached them which lead to problems during the iteration loop. Closes #8458 Closes #8471 Introduced by 0c600cf
1 parent b30ddfb commit e2b1e15

File tree

3 files changed

+37
-10
lines changed

3 files changed

+37
-10
lines changed

modules/@angular/core/src/linker/view.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -192,13 +192,7 @@ export abstract class AppView<T> {
192192
ObservableWrapper.dispose(this.subscriptions[i]);
193193
}
194194
this.destroyInternal();
195-
if (this._hasExternalHostElement) {
196-
this.renderer.detachView(this.flatRootNodes);
197-
} else if (isPresent(this.viewContainerElement)) {
198-
this.viewContainerElement.detachView(this.viewContainerElement.nestedViews.indexOf(this));
199-
} else {
200-
this.dirtyParentQueriesInternal();
201-
}
195+
this.dirtyParentQueriesInternal();
202196
this.renderer.destroyView(hostElement, this.allNodes);
203197
}
204198

modules/@angular/core/test/linker/change_detection_integration_spec.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1005,14 +1005,18 @@ export function main() {
10051005
}));
10061006

10071007
it('should be called after processing the content and view children', fakeAsync(() => {
1008-
var ctx = createCompWithContentAndViewChild();
1008+
var ctx = createCompFixture(
1009+
'<div testDirective="parent"><div *ngFor="let x of [0,1]" testDirective="contentChild{{x}}"></div>' +
1010+
'<other-cmp></other-cmp></div>',
1011+
TestComponent,
1012+
tcb.overrideTemplate(AnotherComponent, '<div testDirective="viewChild"></div>'));
10091013

10101014
ctx.detectChanges(false);
10111015
ctx.destroy();
10121016

10131017
expect(directiveLog.filter(['ngOnDestroy']))
10141018
.toEqual(
1015-
['contentChild.ngOnDestroy', 'viewChild.ngOnDestroy', 'parent.ngOnDestroy']);
1019+
['contentChild0.ngOnDestroy', 'contentChild1.ngOnDestroy', 'viewChild.ngOnDestroy', 'parent.ngOnDestroy']);
10161020
}));
10171021

10181022
it('should be called in reverse order so the child is always notified before the parent',

modules/@angular/core/test/linker/integration_spec.ts

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,35 @@ function declareTests(isJit: boolean) {
483483
});
484484
}));
485485

486+
it('should not detach views in ViewContainers when the parent view is destroyed.',
487+
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
488+
tcb.overrideView(
489+
MyComp, new ViewMetadata({
490+
template:
491+
'<div *ngIf="ctxBoolProp"><template some-viewport let-greeting="someTmpl"><span>{{greeting}}</span></template></div>',
492+
directives: [SomeViewport]
493+
}))
494+
495+
.createAsync(MyComp)
496+
.then((fixture) => {
497+
fixture.debugElement.componentInstance.ctxBoolProp = true;
498+
fixture.detectChanges();
499+
500+
var ngIfEl = fixture.debugElement.children[0];
501+
var someViewport:SomeViewport = ngIfEl.childNodes[0].inject(SomeViewport);
502+
expect(someViewport.container.length).toBe(2);
503+
expect(ngIfEl.children.length).toBe(2);
504+
505+
fixture.debugElement.componentInstance.ctxBoolProp = false;
506+
fixture.detectChanges();
507+
508+
expect(someViewport.container.length).toBe(2);
509+
expect(fixture.debugElement.children.length).toBe(0);
510+
511+
async.done();
512+
});
513+
}));
514+
486515
it('should use a comment while stamping out `<template>` elements.',
487516
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
488517
tcb.overrideView(MyComp, new ViewMetadata({template: '<template></template>'}))
@@ -2165,7 +2194,7 @@ class SomeViewportContext {
21652194
@Directive({selector: '[some-viewport]'})
21662195
@Injectable()
21672196
class SomeViewport {
2168-
constructor(container: ViewContainerRef, templateRef: TemplateRef<SomeViewportContext>) {
2197+
constructor(public container: ViewContainerRef, templateRef: TemplateRef<SomeViewportContext>) {
21692198
container.createEmbeddedView(templateRef, new SomeViewportContext('hello'));
21702199
container.createEmbeddedView(templateRef, new SomeViewportContext('again'));
21712200
}

0 commit comments

Comments
 (0)