fix(ivy): ViewRef.detachFromAppRef should clean the DOM (#29159)

PR Close #29159
This commit is contained in:
Marc Laval 2019-03-07 16:09:12 +01:00 committed by Kara Erickson
parent 29f57e315e
commit eccbc785b3
5 changed files with 73 additions and 10 deletions

View File

@ -243,6 +243,15 @@ export function addRemoveViewFromContainer(
}
}
/**
* Detach a `LView` from the DOM by detaching its nodes.
*
* @param lView the `LView` to be detached.
*/
export function renderDetachView(lView: LView) {
walkTNodeTree(lView, WalkTNodeTreeAction.Detach, lView[RENDERER], null);
}
/**
* Traverses down and up the tree of views and containers to remove listeners and
* call onDestroy callbacks.

View File

@ -14,7 +14,7 @@ import {EmbeddedViewRef as viewEngine_EmbeddedViewRef, InternalViewRef as viewEn
import {checkNoChangesInRootView, checkNoChangesInternal, detectChangesInRootView, detectChangesInternal, markViewDirty, storeCleanupFn} from './instructions';
import {TElementNode, TNode, TNodeType, TViewNode} from './interfaces/node';
import {FLAGS, HOST, LView, LViewFlags, T_HOST} from './interfaces/view';
import {destroyLView} from './node_manipulation';
import {destroyLView, renderDetachView} from './node_manipulation';
import {findComponentView, getLViewParent} from './util/view_traversal_utils';
import {getNativeByTNode} from './util/view_utils';
@ -262,7 +262,10 @@ export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T>, viewEngine_Int
this._viewContainerRef = vcRef;
}
detachFromAppRef() { this._appRef = null; }
detachFromAppRef() {
this._appRef = null;
renderDetachView(this._lView);
}
attachToAppRef(appRef: ApplicationRef) {
if (this._viewContainerRef) {

View File

@ -0,0 +1,56 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {ApplicationRef, Component, ComponentFactoryResolver, ComponentRef, ElementRef, Injector, NgModule} from '@angular/core';
import {InternalViewRef} from '@angular/core/src/linker/view_ref';
import {TestBed} from '@angular/core/testing';
describe('ViewRef', () => {
it('should remove nodes from DOM when the view is detached from app ref', () => {
@Component({selector: 'dynamic-cpt', template: '<div></div>'})
class DynamicComponent {
constructor(public elRef: ElementRef) {}
}
@Component({template: `<span></span>`})
class App {
componentRef !: ComponentRef<DynamicComponent>;
constructor(
public appRef: ApplicationRef, private cfr: ComponentFactoryResolver,
private injector: Injector) {}
create() {
const componentFactory = this.cfr.resolveComponentFactory(DynamicComponent);
this.componentRef = componentFactory.create(this.injector);
(this.componentRef.hostView as InternalViewRef).attachToAppRef(this.appRef);
document.body.appendChild(this.componentRef.instance.elRef.nativeElement);
}
destroy() { (this.componentRef.hostView as InternalViewRef).detachFromAppRef(); }
}
@NgModule({declarations: [App, DynamicComponent], entryComponents: [DynamicComponent]})
class MyTestModule {
}
TestBed.configureTestingModule({imports: [MyTestModule]});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
const appComponent = fixture.componentInstance;
appComponent.create();
fixture.detectChanges();
expect(document.body.querySelector('dynamic-cpt')).not.toBeUndefined();
appComponent.destroy();
fixture.detectChanges();
expect(document.body.querySelector('dynamic-cpt')).toBeUndefined();
});
});

View File

@ -1145,6 +1145,9 @@
{
"name": "renderComponentOrTemplate"
},
{
"name": "renderDetachView"
},
{
"name": "renderEmbeddedTemplate"
},

View File

@ -17,18 +17,10 @@
// tslint:disable
window.testBlocklist = {
"Portals DomPortalOutlet should attach and detach a component portal without a ViewContainerRef": {
"error": "Error: Expected '<pizza-msg><p>Pizza</p><p>Chocolate</p></pizza-msg>' to be '', 'Expected the DomPortalOutlet to be empty after detach'.",
"notes": "Unknown"
},
"CdkTable should be able to render multiple header and footer rows": {
"error": "Error: Missing definitions for header, footer, and row; cannot determine which columns should be rendered.",
"notes": "Attempting to access content children before view is initialized"
},
"CdkTable should be able to render and change multiple header and footer rows": {
"error": "Error: Missing definitions for header, footer, and row; cannot determine which columns should be rendered.",
"notes": "Attempting to access content children before view is initialized"
},
"CdkTable should render correctly when using native HTML tags": {
"error": "Error: Missing definitions for header, footer, and row; cannot determine which columns should be rendered.",
"notes": "Unknown"