fix(ivy): properly destroy views created by ComponentFactory (#27676)
PR Close #27676
This commit is contained in:
parent
f1c9d6a81f
commit
b00aeeff37
|
@ -22,11 +22,10 @@ import {assertComponentType, assertDefined} from './assert';
|
|||
import {LifecycleHooksFeature, createRootComponent, createRootComponentView, createRootContext} from './component';
|
||||
import {getComponentDef} from './definition';
|
||||
import {NodeInjector} from './di';
|
||||
import {createLView, createNodeAtIndex, createTView, createViewNode, elementCreate, locateHostElement, refreshDescendantViews} from './instructions';
|
||||
import {addToViewTree, createLView, createNodeAtIndex, createTView, createViewNode, elementCreate, locateHostElement, refreshDescendantViews} from './instructions';
|
||||
import {ComponentDef, RenderFlags} from './interfaces/definition';
|
||||
import {TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeType} from './interfaces/node';
|
||||
import {RElement, RendererFactory3, domRendererFactory3, isProceduralRenderer} from './interfaces/renderer';
|
||||
import {SanitizerFn} from './interfaces/sanitization';
|
||||
import {HEADER_OFFSET, LView, LViewFlags, RootContext, TVIEW} from './interfaces/view';
|
||||
import {enterView, leaveView} from './state';
|
||||
import {defaultScheduler, getTNode} from './util';
|
||||
|
@ -169,6 +168,7 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
|
|||
|
||||
const componentView = createRootComponentView(
|
||||
hostRNode, this.componentDef, rootLView, rendererFactory, renderer);
|
||||
|
||||
tElementNode = getTNode(0, rootLView) as TElementNode;
|
||||
|
||||
// Transform the arrays of native nodes into a structure that can be consumed by the
|
||||
|
@ -207,6 +207,8 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
|
|||
component = createRootComponent(
|
||||
componentView, this.componentDef, rootLView, rootContext, [LifecycleHooksFeature]);
|
||||
|
||||
addToViewTree(rootLView, HEADER_OFFSET, componentView);
|
||||
|
||||
refreshDescendantViews(rootLView, RenderFlags.Create);
|
||||
} finally {
|
||||
leaveView(oldLView, true);
|
||||
|
|
|
@ -455,16 +455,18 @@ export function getParentState(state: LView | LContainer, rootView: LView): LVie
|
|||
}
|
||||
|
||||
/**
|
||||
* Removes all listeners and call all onDestroys in a given view.
|
||||
* Calls onDestroys hooks for all directives and pipes in a given view and then removes all
|
||||
* listeners. Listeners are removed as the last step so events delivered in the onDestroys hooks
|
||||
* can be propagated to @Output listeners.
|
||||
*
|
||||
* @param view The LView to clean up
|
||||
*/
|
||||
function cleanUpView(viewOrContainer: LView | LContainer): void {
|
||||
if ((viewOrContainer as LView).length >= HEADER_OFFSET) {
|
||||
const view = viewOrContainer as LView;
|
||||
removeListeners(view);
|
||||
executeOnDestroys(view);
|
||||
executePipeOnDestroys(view);
|
||||
removeListeners(view);
|
||||
const hostTNode = view[HOST_NODE];
|
||||
// For component views only, the local renderer is destroyed as clean up time.
|
||||
if (hostTNode && hostTNode.type === TNodeType.Element && isProceduralRenderer(view[RENDERER])) {
|
||||
|
|
|
@ -1104,8 +1104,7 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [
|
|||
});
|
||||
|
||||
describe('ngOnDestroy', () => {
|
||||
fixmeIvy('FW-763: LView tree not properly constructed / destroyed')
|
||||
.it('should be called on view destruction', fakeAsync(() => {
|
||||
it('should be called on view destruction', fakeAsync(() => {
|
||||
const ctx = createCompFixture('<div testDirective="dir"></div>');
|
||||
ctx.detectChanges(false);
|
||||
|
||||
|
@ -1114,13 +1113,10 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [
|
|||
expect(directiveLog.filter(['ngOnDestroy'])).toEqual(['dir.ngOnDestroy']);
|
||||
}));
|
||||
|
||||
fixmeIvy('FW-763: LView tree not properly constructed / destroyed')
|
||||
.it('should be called after processing the content and view children', fakeAsync(() => {
|
||||
it('should be called after processing the content and view children', fakeAsync(() => {
|
||||
TestBed.overrideComponent(AnotherComponent, {
|
||||
set: new Component({
|
||||
selector: 'other-cmp',
|
||||
template: '<div testDirective="viewChild"></div>'
|
||||
})
|
||||
set: new Component(
|
||||
{selector: 'other-cmp', template: '<div testDirective="viewChild"></div>'})
|
||||
});
|
||||
|
||||
const ctx = createCompFixture(
|
||||
|
@ -1132,13 +1128,12 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [
|
|||
ctx.destroy();
|
||||
|
||||
expect(directiveLog.filter(['ngOnDestroy'])).toEqual([
|
||||
'contentChild0.ngOnDestroy', 'contentChild1.ngOnDestroy',
|
||||
'viewChild.ngOnDestroy', 'parent.ngOnDestroy'
|
||||
'contentChild0.ngOnDestroy', 'contentChild1.ngOnDestroy', 'viewChild.ngOnDestroy',
|
||||
'parent.ngOnDestroy'
|
||||
]);
|
||||
}));
|
||||
|
||||
fixmeIvy('FW-763: LView tree not properly constructed / destroyed')
|
||||
.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',
|
||||
fakeAsync(() => {
|
||||
const ctx = createCompFixture(
|
||||
'<div testDirective="parent"><div testDirective="child"></div></div><div testDirective="sibling"></div>');
|
||||
|
@ -1151,10 +1146,8 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [
|
|||
]);
|
||||
}));
|
||||
|
||||
fixmeIvy('FW-763: LView tree not properly constructed / destroyed')
|
||||
.it('should deliver synchronous events to parent', fakeAsync(() => {
|
||||
const ctx =
|
||||
createCompFixture('<div (destroy)="a=$event" onDestroyDirective></div>');
|
||||
it('should deliver synchronous events to parent', fakeAsync(() => {
|
||||
const ctx = createCompFixture('<div (destroy)="a=$event" onDestroyDirective></div>');
|
||||
|
||||
ctx.detectChanges(false);
|
||||
ctx.destroy();
|
||||
|
@ -1162,8 +1155,8 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [
|
|||
expect(ctx.componentInstance.a).toEqual('destroyed');
|
||||
}));
|
||||
|
||||
fixmeIvy('FW-763: LView tree not properly constructed / destroyed')
|
||||
.it('should call ngOnDestroy on pipes', fakeAsync(() => {
|
||||
|
||||
it('should call ngOnDestroy on pipes', fakeAsync(() => {
|
||||
const ctx = createCompFixture('{{true | pipeWithOnDestroy }}');
|
||||
|
||||
ctx.detectChanges(false);
|
||||
|
@ -1174,7 +1167,7 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [
|
|||
]);
|
||||
}));
|
||||
|
||||
fixmeIvy('FW-763: LView tree not properly constructed / destroyed')
|
||||
fixmeIvy('FW-848: ngOnDestroy hooks are not called on providers')
|
||||
.it('should call ngOnDestroy on an injectable class', fakeAsync(() => {
|
||||
TestBed.overrideDirective(
|
||||
TestDirective, {set: {providers: [InjectableWithLifecycle]}});
|
||||
|
|
|
@ -768,9 +768,7 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
expect(childComponent.myHost).toBeAnInstanceOf(SomeDirective);
|
||||
});
|
||||
|
||||
fixmeIvy(
|
||||
'FW-763: LView tree not properly constructed / destroyed for dynamically inserted components')
|
||||
.it('should support events via EventEmitter on regular elements', async(() => {
|
||||
it('should support events via EventEmitter on regular elements', async(() => {
|
||||
TestBed.configureTestingModule(
|
||||
{declarations: [MyComp, DirectiveEmittingEvent, DirectiveListeningEvent]});
|
||||
const template = '<div emitter listener></div>';
|
||||
|
|
|
@ -616,9 +616,8 @@ describe('Query API', () => {
|
|||
expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '2']);
|
||||
});
|
||||
|
||||
fixmeIvy(
|
||||
'FW-763 - LView tree not properly constructed / destroyed for dynamically inserted components')
|
||||
.it('should remove manually projected templates if their parent view is destroyed', () => {
|
||||
fixmeIvy('unknown').it(
|
||||
'should remove manually projected templates if their parent view is destroyed', () => {
|
||||
const template = `
|
||||
<manual-projecting #q><ng-template #tpl><div text="1"></div></ng-template></manual-projecting>
|
||||
<div *ngIf="shouldShow">
|
||||
|
|
Loading…
Reference in New Issue