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 0c600cf6e3
This commit is contained in:
parent
b30ddfbfc5
commit
e2b1e1577d
|
@ -192,13 +192,7 @@ export abstract class AppView<T> {
|
||||||
ObservableWrapper.dispose(this.subscriptions[i]);
|
ObservableWrapper.dispose(this.subscriptions[i]);
|
||||||
}
|
}
|
||||||
this.destroyInternal();
|
this.destroyInternal();
|
||||||
if (this._hasExternalHostElement) {
|
|
||||||
this.renderer.detachView(this.flatRootNodes);
|
|
||||||
} else if (isPresent(this.viewContainerElement)) {
|
|
||||||
this.viewContainerElement.detachView(this.viewContainerElement.nestedViews.indexOf(this));
|
|
||||||
} else {
|
|
||||||
this.dirtyParentQueriesInternal();
|
this.dirtyParentQueriesInternal();
|
||||||
}
|
|
||||||
this.renderer.destroyView(hostElement, this.allNodes);
|
this.renderer.destroyView(hostElement, this.allNodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1005,14 +1005,18 @@ export function main() {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should be called after processing the content and view children', fakeAsync(() => {
|
it('should be called after processing the content and view children', fakeAsync(() => {
|
||||||
var ctx = createCompWithContentAndViewChild();
|
var ctx = createCompFixture(
|
||||||
|
'<div testDirective="parent"><div *ngFor="let x of [0,1]" testDirective="contentChild{{x}}"></div>' +
|
||||||
|
'<other-cmp></other-cmp></div>',
|
||||||
|
TestComponent,
|
||||||
|
tcb.overrideTemplate(AnotherComponent, '<div testDirective="viewChild"></div>'));
|
||||||
|
|
||||||
ctx.detectChanges(false);
|
ctx.detectChanges(false);
|
||||||
ctx.destroy();
|
ctx.destroy();
|
||||||
|
|
||||||
expect(directiveLog.filter(['ngOnDestroy']))
|
expect(directiveLog.filter(['ngOnDestroy']))
|
||||||
.toEqual(
|
.toEqual(
|
||||||
['contentChild.ngOnDestroy', 'viewChild.ngOnDestroy', 'parent.ngOnDestroy']);
|
['contentChild0.ngOnDestroy', 'contentChild1.ngOnDestroy', 'viewChild.ngOnDestroy', 'parent.ngOnDestroy']);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should be called in reverse order so the child is always notified before the parent',
|
it('should be called in reverse order so the child is always notified before the parent',
|
||||||
|
|
|
@ -483,6 +483,35 @@ function declareTests(isJit: boolean) {
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it('should not detach views in ViewContainers when the parent view is destroyed.',
|
||||||
|
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||||
|
tcb.overrideView(
|
||||||
|
MyComp, new ViewMetadata({
|
||||||
|
template:
|
||||||
|
'<div *ngIf="ctxBoolProp"><template some-viewport let-greeting="someTmpl"><span>{{greeting}}</span></template></div>',
|
||||||
|
directives: [SomeViewport]
|
||||||
|
}))
|
||||||
|
|
||||||
|
.createAsync(MyComp)
|
||||||
|
.then((fixture) => {
|
||||||
|
fixture.debugElement.componentInstance.ctxBoolProp = true;
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
var ngIfEl = fixture.debugElement.children[0];
|
||||||
|
var someViewport:SomeViewport = ngIfEl.childNodes[0].inject(SomeViewport);
|
||||||
|
expect(someViewport.container.length).toBe(2);
|
||||||
|
expect(ngIfEl.children.length).toBe(2);
|
||||||
|
|
||||||
|
fixture.debugElement.componentInstance.ctxBoolProp = false;
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(someViewport.container.length).toBe(2);
|
||||||
|
expect(fixture.debugElement.children.length).toBe(0);
|
||||||
|
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
it('should use a comment while stamping out `<template>` elements.',
|
it('should use a comment while stamping out `<template>` elements.',
|
||||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||||
tcb.overrideView(MyComp, new ViewMetadata({template: '<template></template>'}))
|
tcb.overrideView(MyComp, new ViewMetadata({template: '<template></template>'}))
|
||||||
|
@ -2165,7 +2194,7 @@ class SomeViewportContext {
|
||||||
@Directive({selector: '[some-viewport]'})
|
@Directive({selector: '[some-viewport]'})
|
||||||
@Injectable()
|
@Injectable()
|
||||||
class SomeViewport {
|
class SomeViewport {
|
||||||
constructor(container: ViewContainerRef, templateRef: TemplateRef<SomeViewportContext>) {
|
constructor(public container: ViewContainerRef, templateRef: TemplateRef<SomeViewportContext>) {
|
||||||
container.createEmbeddedView(templateRef, new SomeViewportContext('hello'));
|
container.createEmbeddedView(templateRef, new SomeViewportContext('hello'));
|
||||||
container.createEmbeddedView(templateRef, new SomeViewportContext('again'));
|
container.createEmbeddedView(templateRef, new SomeViewportContext('again'));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue