From 44373ee8e35836bfc6593cffa98d1ef4d4099149 Mon Sep 17 00:00:00 2001 From: Jeremy Elbourn Date: Fri, 12 Aug 2016 13:48:43 -0700 Subject: [PATCH 1/4] wip remove component resolver --- src/core/overlay/overlay.spec.ts | 39 +++++++++------------------- src/core/overlay/overlay.ts | 6 ++--- src/core/portal/dom-portal-host.ts | 28 ++++++++++---------- src/core/portal/portal-directives.ts | 20 +++++++------- src/core/portal/portal.spec.ts | 12 +++++---- 5 files changed, 45 insertions(+), 60 deletions(-) diff --git a/src/core/overlay/overlay.spec.ts b/src/core/overlay/overlay.spec.ts index 852c0e441804..ac439632c201 100644 --- a/src/core/overlay/overlay.spec.ts +++ b/src/core/overlay/overlay.spec.ts @@ -1,11 +1,4 @@ -import { - inject, - fakeAsync, - flushMicrotasks, - TestComponentBuilder, - TestBed, - async, -} from '@angular/core/testing'; +import {inject, fakeAsync, flushMicrotasks, TestBed, async} from '@angular/core/testing'; import {Component, ViewChild, ViewContainerRef} from '@angular/core'; import {TemplatePortalDirective, PortalModule} from '../portal/portal-directives'; import {TemplatePortal, ComponentPortal} from '../portal/portal'; @@ -17,8 +10,7 @@ import {PositionStrategy} from './position/position-strategy'; import {OverlayModule} from './overlay-directives'; -describe('Overlay', () => { - let builder: TestComponentBuilder; +fdescribe('Overlay', () => { let overlay: Overlay; let componentPortal: ComponentPortal; let templatePortal: TemplatePortal; @@ -35,24 +27,23 @@ describe('Overlay', () => { }} ] }); + + TestBed.compileComponents(); })); - let deps = [TestComponentBuilder, Overlay]; - beforeEach(fakeAsync(inject(deps, (tcb: TestComponentBuilder, o: Overlay) => { - builder = tcb; + beforeEach(fakeAsync(inject([Overlay], (o: Overlay) => { overlay = o; - builder.createAsync(TestComponentWithTemplatePortals).then(fixture => { - fixture.detectChanges(); - templatePortal = fixture.componentInstance.templatePortal; - componentPortal = new ComponentPortal(PizzaMsg, fixture.componentInstance.viewContainerRef); - }); + let fixture = TestBed.createComponent(TestComponentWithTemplatePortals); + fixture.detectChanges(); + templatePortal = fixture.componentInstance.templatePortal; + componentPortal = new ComponentPortal(PizzaMsg, fixture.componentInstance.viewContainerRef); flushMicrotasks(); }))); - it('should load a component into an overlay', fakeAsync(() => { + fit('should load a component into an overlay', fakeAsync(() => { let overlayRef: OverlayRef; overlay.create().then(ref => { @@ -140,18 +131,12 @@ describe('Overlay', () => { /** Simple component for testing ComponentPortal. */ -@Component({ - selector: 'pizza-msg', - template: '

Pizza

', -}) +@Component({template: '

Pizza

'}) class PizzaMsg { } /** Test-bed component that contains a TempatePortal and an ElementRef. */ -@Component({ - selector: 'portal-test', - template: ``, -}) +@Component({template: ``}) class TestComponentWithTemplatePortals { @ViewChild(TemplatePortalDirective) templatePortal: TemplatePortalDirective; diff --git a/src/core/overlay/overlay.ts b/src/core/overlay/overlay.ts index b662a9317204..692d283bdc3c 100644 --- a/src/core/overlay/overlay.ts +++ b/src/core/overlay/overlay.ts @@ -1,5 +1,5 @@ import { - ComponentResolver, + ComponentFactoryResolver, Injectable, } from '@angular/core'; import {OverlayState} from './overlay-state'; @@ -28,7 +28,7 @@ let defaultState = new OverlayState(); @Injectable() export class Overlay { constructor(private _overlayContainer: OverlayContainer, - private _componentResolver: ComponentResolver, + private _componentFactoryResolver: ComponentFactoryResolver, private _positionBuilder: OverlayPositionBuilder) {} /** @@ -68,7 +68,7 @@ export class Overlay { * @returns A portal host for the given DOM element. */ private _createPortalHost(pane: HTMLElement): DomPortalHost { - return new DomPortalHost(pane, this._componentResolver); + return new DomPortalHost(pane, this._componentFactoryResolver); } /** diff --git a/src/core/portal/dom-portal-host.ts b/src/core/portal/dom-portal-host.ts index 2e9a3d8bd478..a4606cec7af9 100644 --- a/src/core/portal/dom-portal-host.ts +++ b/src/core/portal/dom-portal-host.ts @@ -1,4 +1,4 @@ -import {ComponentResolver, ComponentRef, EmbeddedViewRef} from '@angular/core'; +import {ComponentFactoryResolver, ComponentRef, EmbeddedViewRef} from '@angular/core'; import {BasePortalHost, ComponentPortal, TemplatePortal} from './portal'; import {MdComponentPortalAttachedToDomWithoutOriginError} from './portal-errors'; @@ -12,27 +12,27 @@ import {MdComponentPortalAttachedToDomWithoutOriginError} from './portal-errors' export class DomPortalHost extends BasePortalHost { constructor( private _hostDomElement: Element, - private _componentResolver: ComponentResolver) { + private _componentFactoryResolver: ComponentFactoryResolver) { super(); } - /** Attach the given ComponentPortal to DOM element using the ComponentResolver. */ + /** Attach the given ComponentPortal to DOM element using the ComponentFactoryResolver. */ attachComponentPortal(portal: ComponentPortal): Promise> { if (portal.viewContainerRef == null) { throw new MdComponentPortalAttachedToDomWithoutOriginError(); } - return this._componentResolver.resolveComponent(portal.component).then(componentFactory => { - let ref = portal.viewContainerRef.createComponent( - componentFactory, - portal.viewContainerRef.length, - portal.injector || portal.viewContainerRef.parentInjector); - - let hostView = > ref.hostView; - this._hostDomElement.appendChild(hostView.rootNodes[0]); - this.setDisposeFn(() => ref.destroy()); - return ref; - }); + let componentFactory = this._componentFactoryResolver.resolveComponentFactory(portal.component); + let ref = portal.viewContainerRef.createComponent( + componentFactory, + portal.viewContainerRef.length, + portal.injector || portal.viewContainerRef.parentInjector); + + let hostView = > ref.hostView; + this._hostDomElement.appendChild(hostView.rootNodes[0]); + this.setDisposeFn(() => ref.destroy()); + + return Promise.resolve(ref); } attachTemplatePortal(portal: TemplatePortal): Promise> { diff --git a/src/core/portal/portal-directives.ts b/src/core/portal/portal-directives.ts index 90fa13afa4e9..64690d382402 100644 --- a/src/core/portal/portal-directives.ts +++ b/src/core/portal/portal-directives.ts @@ -3,13 +3,12 @@ import { ComponentRef, Directive, TemplateRef, - ComponentResolver, + ComponentFactoryResolver, ViewContainerRef } from '@angular/core'; import {Portal, TemplatePortal, ComponentPortal, BasePortalHost} from './portal'; - /** * Directive version of a `TemplatePortal`. Because the directive *is* a TemplatePortal, * the directive instance itself can be attached to a host, enabling declarative use of portals. @@ -46,7 +45,7 @@ export class PortalHostDirective extends BasePortalHost { private _portal: Portal; constructor( - private _componentResolver: ComponentResolver, + private _componentFactoryResolver: ComponentFactoryResolver, private _viewContainerRef: ViewContainerRef) { super(); } @@ -59,7 +58,7 @@ export class PortalHostDirective extends BasePortalHost { this._replaceAttachedPortal(p); } - /** Attach the given ComponentPortal to this PortlHost using the ComponentResolver. */ + /** Attach the given ComponentPortal to this PortlHost using the ComponentFactoryResolver. */ attachComponentPortal(portal: ComponentPortal): Promise> { portal.setAttachedHost(this); @@ -69,14 +68,13 @@ export class PortalHostDirective extends BasePortalHost { portal.viewContainerRef : this._viewContainerRef; - return this._componentResolver.resolveComponent(portal.component).then(componentFactory => { - let ref = viewContainerRef.createComponent( - componentFactory, viewContainerRef.length, - portal.injector || viewContainerRef.parentInjector); + let componentFactory = this._componentFactoryResolver.resolveComponentFactory(portal.component) + let ref = viewContainerRef.createComponent( + componentFactory, viewContainerRef.length, + portal.injector || viewContainerRef.parentInjector); - this.setDisposeFn(() => ref.destroy()); - return ref; - }); + this.setDisposeFn(() => ref.destroy()); + return Promise.resolve(ref); } /** Attach the given TemplatePortal to this PortlHost as an embedded View. */ diff --git a/src/core/portal/portal.spec.ts b/src/core/portal/portal.spec.ts index 0409c8c527af..5cde3e7de351 100644 --- a/src/core/portal/portal.spec.ts +++ b/src/core/portal/portal.spec.ts @@ -12,7 +12,7 @@ import { ViewChildren, QueryList, ViewContainerRef, - ComponentResolver, + ComponentFactoryResolver, Optional, Injector, } from '@angular/core'; @@ -29,6 +29,8 @@ describe('Portals', () => { imports: [PortalModule], declarations: [PortalTestApp, ArbitraryViewContainerRefComponent, PizzaMsg], }); + + TestBed.compileComponents(); })); beforeEach(fakeAsync(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => { @@ -216,19 +218,19 @@ describe('Portals', () => { }); describe('DomPortalHost', function () { - let componentLoader: ComponentResolver; + let componentFactoryResolver: ComponentFactoryResolver; let someViewContainerRef: ViewContainerRef; let someInjector: Injector; let someDomElement: HTMLElement; let host: DomPortalHost; - beforeEach(inject([ComponentResolver], (dcl: ComponentResolver) => { - componentLoader = dcl; + beforeEach(inject([ComponentFactoryResolver], (dcl: ComponentFactoryResolver) => { + componentFactoryResolver = dcl; })); beforeEach(() => { someDomElement = document.createElement('div'); - host = new DomPortalHost(someDomElement, componentLoader); + host = new DomPortalHost(someDomElement, componentFactoryResolver); }); it('should attach and detach a component portal', fakeAsync(() => { From 8bad42f9fd7d82e1a065b5c2dce4e6e43f975793 Mon Sep 17 00:00:00 2001 From: Jeremy Elbourn Date: Fri, 12 Aug 2016 16:26:58 -0700 Subject: [PATCH 2/4] portal and overlay --- src/core/overlay/overlay.spec.ts | 20 ++- src/core/portal/portal.spec.ts | 205 ++++++++++--------------------- 2 files changed, 79 insertions(+), 146 deletions(-) diff --git a/src/core/overlay/overlay.spec.ts b/src/core/overlay/overlay.spec.ts index ac439632c201..84a0f174487d 100644 --- a/src/core/overlay/overlay.spec.ts +++ b/src/core/overlay/overlay.spec.ts @@ -1,5 +1,5 @@ import {inject, fakeAsync, flushMicrotasks, TestBed, async} from '@angular/core/testing'; -import {Component, ViewChild, ViewContainerRef} from '@angular/core'; +import {NgModule, Component, ViewChild, ViewContainerRef} from '@angular/core'; import {TemplatePortalDirective, PortalModule} from '../portal/portal-directives'; import {TemplatePortal, ComponentPortal} from '../portal/portal'; import {Overlay} from './overlay'; @@ -10,7 +10,7 @@ import {PositionStrategy} from './position/position-strategy'; import {OverlayModule} from './overlay-directives'; -fdescribe('Overlay', () => { +describe('Overlay', () => { let overlay: Overlay; let componentPortal: ComponentPortal; let templatePortal: TemplatePortal; @@ -18,8 +18,7 @@ fdescribe('Overlay', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [OverlayModule, PortalModule], - declarations: [TestComponentWithTemplatePortals, PizzaMsg], + imports: [OverlayModule, PortalModule, OverlayTestModule], providers: [ {provide: OverlayContainer, useFactory: () => { overlayContainerElement = document.createElement('div'); @@ -31,7 +30,6 @@ fdescribe('Overlay', () => { TestBed.compileComponents(); })); - beforeEach(fakeAsync(inject([Overlay], (o: Overlay) => { overlay = o; @@ -43,7 +41,7 @@ fdescribe('Overlay', () => { flushMicrotasks(); }))); - fit('should load a component into an overlay', fakeAsync(() => { + it('should load a component into an overlay', fakeAsync(() => { let overlayRef: OverlayRef; overlay.create().then(ref => { @@ -143,6 +141,16 @@ class TestComponentWithTemplatePortals { constructor(public viewContainerRef: ViewContainerRef) { } } +// Create a real (non-test) NgModule as a workaround for +// https://github.com/angular/angular/issues/10760 +@NgModule({ + imports: [OverlayModule, PortalModule], + exports: [PizzaMsg, TestComponentWithTemplatePortals], + declarations: [PizzaMsg, TestComponentWithTemplatePortals], + entryComponents: [PizzaMsg, TestComponentWithTemplatePortals], +}) +class OverlayTestModule { } + class FakePositionStrategy implements PositionStrategy { apply(element: Element): Promise { element.classList.add('fake-positioned'); diff --git a/src/core/portal/portal.spec.ts b/src/core/portal/portal.spec.ts index 5cde3e7de351..031367179791 100644 --- a/src/core/portal/portal.spec.ts +++ b/src/core/portal/portal.spec.ts @@ -2,12 +2,12 @@ import { inject, fakeAsync, flushMicrotasks, - TestComponentBuilder, ComponentFixture, TestBed, async, } from '@angular/core/testing'; import { + NgModule, Component, ViewChildren, QueryList, @@ -22,194 +22,140 @@ import {DomPortalHost} from './dom-portal-host'; describe('Portals', () => { - let builder: TestComponentBuilder; beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [PortalModule], - declarations: [PortalTestApp, ArbitraryViewContainerRefComponent, PizzaMsg], + imports: [PortalModule, PortalTestModule], }); TestBed.compileComponents(); })); - beforeEach(fakeAsync(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => { - builder = tcb; - }))); - describe('PortalHostDirective', () => { - it('should load a component into the portal', fakeAsync(() => { - let appFixture: ComponentFixture; + let fixture: ComponentFixture; - builder.createAsync(PortalTestApp).then(fixture => { - appFixture = fixture; - }); - - // Flush the async creation of the PortalTestApp. - flushMicrotasks(); + beforeEach(fakeAsync(() => { + fixture = TestBed.createComponent(PortalTestApp); + })); + it('should load a component into the portal', fakeAsync(() => { // Set the selectedHost to be a ComponentPortal. - let testAppComponent = appFixture.debugElement.componentInstance; + let testAppComponent = fixture.debugElement.componentInstance; testAppComponent.selectedPortal = new ComponentPortal(PizzaMsg); - appFixture.detectChanges(); + fixture.detectChanges(); // Flush the attachment of the Portal. flushMicrotasks(); // Expect that the content of the attached portal is present. - let hostContainer = appFixture.nativeElement.querySelector('.portal-container'); + let hostContainer = fixture.nativeElement.querySelector('.portal-container'); expect(hostContainer.textContent).toContain('Pizza'); })); it('should load a component into the portal with a given injector', fakeAsync(() => { - let appFixture: ComponentFixture; - - builder.createAsync(PortalTestApp).then(fixture => { - appFixture = fixture; - }); - - // Flush the async creation of the PortalTestApp. - flushMicrotasks(); - // Create a custom injector for the component. - let chocolateInjector = new ChocolateInjector(appFixture.componentInstance.injector); + let chocolateInjector = new ChocolateInjector(fixture.componentInstance.injector); // Set the selectedHost to be a ComponentPortal. - let testAppComponent = appFixture.debugElement.componentInstance; + let testAppComponent = fixture.debugElement.componentInstance; testAppComponent.selectedPortal = new ComponentPortal(PizzaMsg, null, chocolateInjector); - appFixture.detectChanges(); + fixture.detectChanges(); // Flush the attachment of the Portal. flushMicrotasks(); - appFixture.detectChanges(); + fixture.detectChanges(); // Expect that the content of the attached portal is present. - let hostContainer = appFixture.nativeElement.querySelector('.portal-container'); + let hostContainer = fixture.nativeElement.querySelector('.portal-container'); expect(hostContainer.textContent).toContain('Pizza'); expect(hostContainer.textContent).toContain('Chocolate'); })); it('should load a