Skip to content

Commit

Permalink
fix(overlay): remove overlay container on destroy (#5378)
Browse files Browse the repository at this point in the history
  • Loading branch information
jelbourn authored and kara committed Aug 22, 2017
1 parent 363562f commit 154bb55
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 2 deletions.
10 changes: 8 additions & 2 deletions src/cdk/overlay/overlay-container.ts
Expand Up @@ -6,15 +6,15 @@
* found in the LICENSE file at https://angular.io/license
*/

import {Injectable, Optional, SkipSelf} from '@angular/core';
import {Injectable, Optional, SkipSelf, OnDestroy} from '@angular/core';


/**
* The OverlayContainer is the container in which all overlays will load.
* It should be provided in the root component to ensure it is properly shared.
*/
@Injectable()
export class OverlayContainer {
export class OverlayContainer implements OnDestroy {
protected _containerElement: HTMLElement;

private _themeClass: string;
Expand All @@ -37,6 +37,12 @@ export class OverlayContainer {
this._themeClass = value;
}

ngOnDestroy() {
if (this._containerElement && this._containerElement.parentNode) {
this._containerElement.parentNode.removeChild(this._containerElement);
}
}

/**
* This method returns the overlay container element. It will lazily
* create the element the first time it is called to facilitate using
Expand Down
63 changes: 63 additions & 0 deletions src/lib/core/overlay/overlay-container.spec.ts
@@ -0,0 +1,63 @@
import {async, inject, TestBed} from '@angular/core/testing';
import {Component, NgModule, ViewChild, ViewContainerRef} from '@angular/core';
import {PortalModule, TemplatePortalDirective} from '../portal/portal-directives';
import {Overlay, OverlayContainer, OverlayModule} from './index';

describe('OverlayContainer', () => {
let overlay: Overlay;
let overlayContainer: OverlayContainer;

beforeAll(() => {
// Remove any stale overlay containers from previous tests that didn't clean up correctly.
const staleContainers = document.querySelectorAll('.cdk-overlay-container');
for (let i = staleContainers.length - 1; i >= 0; i--) {
staleContainers[i].parentNode!.removeChild(staleContainers[i]);
}
});

beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [OverlayTestModule]
}).compileComponents();
}));

beforeEach(inject([Overlay, OverlayContainer], (o: Overlay, oc: OverlayContainer) => {
overlay = o;
overlayContainer = oc;
}));

it('should remove the overlay container element from the DOM on destruction', () => {
const fixture = TestBed.createComponent(TestComponentWithTemplatePortals);

const overlayRef = overlay.create();
overlayRef.attach(fixture.componentInstance.templatePortal);
fixture.detectChanges();

expect(document.querySelectorAll('.cdk-overlay-container'))
.not.toBeNull('Expected the overlay container to be in the DOM after opening an overlay');

// Manually call `ngOnDestroy` because there is no way to force Angular to destroy an
// injectable in a unit test.
overlayContainer.ngOnDestroy();

expect(document.querySelector('.cdk-overlay-container'))
.toBeNull('Expected the overlay container *not* to be in the DOM after destruction');
});
});

/** Test-bed component that contains a TempatePortal and an ElementRef. */
@Component({
template: `<ng-template cdk-portal>Cake</ng-template>`,
providers: [Overlay],
})
class TestComponentWithTemplatePortals {
@ViewChild(TemplatePortalDirective) templatePortal: TemplatePortalDirective;

constructor(public viewContainerRef: ViewContainerRef) { }
}

@NgModule({
imports: [OverlayModule, PortalModule],
declarations: [TestComponentWithTemplatePortals]
})
class OverlayTestModule { }

0 comments on commit 154bb55

Please sign in to comment.