Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(overlay): make overlays synchronous #1079

Merged
merged 2 commits into from
Aug 19, 2016
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions src/demo-app/dialog/dialog-demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,11 @@ export class DialogDemo {
let config = new MdDialogConfig();
config.viewContainerRef = this.viewContainerRef;

this.dialog.open(JazzDialog, config).then(ref => {
this.dialogRef = ref;
this.dialogRef = this.dialog.open(JazzDialog, config);

this.dialogRef.afterClosed().subscribe(result => {
this.lastCloseResult = result;
this.dialogRef = null;
});
this.dialogRef.afterClosed().subscribe(result => {
this.lastCloseResult = result;
this.dialogRef = null;
});
}
}
Expand Down
15 changes: 6 additions & 9 deletions src/demo-app/overlay/overlay-demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,8 @@ export class OverlayDemo {

this.nextPosition += 30;

this.overlay.create(config).then(ref => {
ref.attach(new ComponentPortal(RotiniPanel, this.viewContainerRef));
});
let overlayRef = this.overlay.create(config);
overlayRef.attach(new ComponentPortal(RotiniPanel, this.viewContainerRef));
}

openFusilliPanel() {
Expand All @@ -59,9 +58,8 @@ export class OverlayDemo {

this.nextPosition += 30;

this.overlay.create(config).then(ref => {
ref.attach(this.templatePortals.first);
});
let overlayRef = this.overlay.create(config);
overlayRef.attach(this.templatePortals.first);
}

openSpaghettiPanel() {
Expand All @@ -75,9 +73,8 @@ export class OverlayDemo {
let config = new OverlayState();
config.positionStrategy = strategy;

this.overlay.create(config).then(ref => {
ref.attach(new ComponentPortal(SpagettiPanel, this.viewContainerRef));
});
let overlayRef = this.overlay.create(config);
overlayRef.attach(new ComponentPortal(SpagettiPanel, this.viewContainerRef));
}
}

Expand Down
6 changes: 2 additions & 4 deletions src/lib/core/overlay/overlay-directives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,8 @@ export class ConnectedOverlayDirective implements OnInit, OnDestroy {
{originX: this.positions[0].overlayX, originY: this.positions[0].originY},
{overlayX: this.positions[0].overlayX, overlayY: this.positions[0].overlayY});

this._overlay.create(overlayConfig).then(ref => {
this._overlayRef = ref;
this._overlayRef.attach(this._templatePortal);
});
this._overlayRef = this._overlay.create(overlayConfig);
this._overlayRef.attach(this._templatePortal);
}

/** Destroys the overlay created by this directive. */
Expand Down
13 changes: 4 additions & 9 deletions src/lib/core/overlay/overlay-ref.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,11 @@ export class OverlayRef implements PortalHost {
private _pane: HTMLElement,
private _state: OverlayState) { }

attach(portal: Portal<any>): Promise<any> {
let attachPromise = this._portalHost.attach(portal);
attach(portal: Portal<any>): any {
let attachResult = this._portalHost.attach(portal);
this.updatePosition();

// Don't chain the .then() call in the return because we want the result of portalHost.attach
// to be returned from this method.
attachPromise.then(() => {
this.updatePosition();
});

return attachPromise;
return attachResult;
}

detach(): Promise<any> {
Expand Down
70 changes: 20 additions & 50 deletions src/lib/core/overlay/overlay.spec.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import {inject, fakeAsync, flushMicrotasks, TestBed, async} from '@angular/core/testing';
import {inject, TestBed, async} from '@angular/core/testing';
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';
import {OverlayContainer} from './overlay-container';
import {OverlayRef} from './overlay-ref';
import {OverlayState} from './overlay-state';
import {PositionStrategy} from './position/position-strategy';
import {OverlayModule} from './overlay-directives';
Expand All @@ -30,68 +29,43 @@ describe('Overlay', () => {
TestBed.compileComponents();
}));

beforeEach(fakeAsync(inject([Overlay], (o: Overlay) => {
beforeEach(inject([Overlay], (o: Overlay) => {
overlay = o;

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(() => {
let overlayRef: OverlayRef;

overlay.create().then(ref => {
overlayRef = ref;
overlayRef.attach(componentPortal);
});

flushMicrotasks();
it('should load a component into an overlay', () => {
let overlayRef = overlay.create();
overlayRef.attach(componentPortal);

expect(overlayContainerElement.textContent).toContain('Pizza');

overlayRef.dispose();
expect(overlayContainerElement.childNodes.length).toBe(0);
expect(overlayContainerElement.textContent).toBe('');
}));

it('should load a template portal into an overlay', fakeAsync(() => {
let overlayRef: OverlayRef;

overlay.create().then(ref => {
overlayRef = ref;
overlayRef.attach(templatePortal);
});
});

flushMicrotasks();
it('should load a template portal into an overlay', () => {
let overlayRef = overlay.create();
overlayRef.attach(templatePortal);

expect(overlayContainerElement.textContent).toContain('Cake');

overlayRef.dispose();
expect(overlayContainerElement.childNodes.length).toBe(0);
expect(overlayContainerElement.textContent).toBe('');
}));

it('should open multiple overlays', fakeAsync(() => {
let pizzaOverlayRef: OverlayRef;
let cakeOverlayRef: OverlayRef;

overlay.create().then(ref => {
pizzaOverlayRef = ref;
pizzaOverlayRef.attach(componentPortal);
});

flushMicrotasks();
});

overlay.create().then(ref => {
cakeOverlayRef = ref;
cakeOverlayRef.attach(templatePortal);
});
it('should open multiple overlays', () => {
let pizzaOverlayRef = overlay.create();
pizzaOverlayRef.attach(componentPortal);

flushMicrotasks();
let cakeOverlayRef = overlay.create();
cakeOverlayRef.attach(templatePortal);

expect(overlayContainerElement.childNodes.length).toBe(2);
expect(overlayContainerElement.textContent).toContain('Pizza');
Expand All @@ -104,7 +78,7 @@ describe('Overlay', () => {
cakeOverlayRef.dispose();
expect(overlayContainerElement.childNodes.length).toBe(0);
expect(overlayContainerElement.textContent).toBe('');
}));
});

describe('applyState', () => {
let state: OverlayState;
Expand All @@ -113,17 +87,13 @@ describe('Overlay', () => {
state = new OverlayState();
});

it('should apply the positioning strategy', fakeAsync(() => {
it('should apply the positioning strategy', () => {
state.positionStrategy = new FakePositionStrategy();

overlay.create(state).then(ref => {
ref.attach(componentPortal);
});

flushMicrotasks();
overlay.create(state).attach(componentPortal);

expect(overlayContainerElement.querySelectorAll('.fake-positioned').length).toBe(1);
}));
});
});
});

Expand Down
8 changes: 4 additions & 4 deletions src/lib/core/overlay/overlay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ export class Overlay {
* @param state State to apply to the overlay.
* @returns A reference to the created overlay.
*/
create(state: OverlayState = defaultState): Promise<OverlayRef> {
return this._createPaneElement().then(pane => this._createOverlayRef(pane, state));
create(state: OverlayState = defaultState): OverlayRef {
return this._createOverlayRef(this._createPaneElement(), state);
}

/**
Expand All @@ -52,14 +52,14 @@ export class Overlay {
* Creates the DOM element for an overlay and appends it to the overlay container.
* @returns Promise resolving to the created element.
*/
private _createPaneElement(): Promise<HTMLElement> {
private _createPaneElement(): HTMLElement {
var pane = document.createElement('div');
pane.id = `md-overlay-${nextUniqueId++}`;
pane.classList.add('md-overlay-pane');

this._overlayContainer.getContainerElement().appendChild(pane);

return Promise.resolve(pane);
return pane;
}

/**
Expand Down
8 changes: 4 additions & 4 deletions src/lib/core/portal/dom-portal-host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export class DomPortalHost extends BasePortalHost {
}

/** Attach the given ComponentPortal to DOM element using the ComponentFactoryResolver. */
attachComponentPortal<T>(portal: ComponentPortal<T>): Promise<ComponentRef<T>> {
attachComponentPortal<T>(portal: ComponentPortal<T>): ComponentRef<T> {
if (portal.viewContainerRef == null) {
throw new MdComponentPortalAttachedToDomWithoutOriginError();
}
Expand All @@ -32,10 +32,10 @@ export class DomPortalHost extends BasePortalHost {
this._hostDomElement.appendChild(hostView.rootNodes[0]);
this.setDisposeFn(() => ref.destroy());

return Promise.resolve(ref);
return ref;
}

attachTemplatePortal(portal: TemplatePortal): Promise<Map<string, any>> {
attachTemplatePortal(portal: TemplatePortal): Map<string, any> {
let viewContainer = portal.viewContainerRef;
let viewRef = viewContainer.createEmbeddedView(portal.templateRef);

Expand All @@ -49,7 +49,7 @@ export class DomPortalHost extends BasePortalHost {
}));

// TODO(jelbourn): Return locals from view.
return Promise.resolve(new Map<string, any>());
return new Map<string, any>();
}

dispose(): void {
Expand Down
24 changes: 12 additions & 12 deletions src/lib/core/portal/portal-directives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export class PortalHostDirective extends BasePortalHost {
}

/** Attach the given ComponentPortal to this PortlHost using the ComponentFactoryResolver. */
attachComponentPortal<T>(portal: ComponentPortal<T>): Promise<ComponentRef<T>> {
attachComponentPortal<T>(portal: ComponentPortal<T>): ComponentRef<T> {
portal.setAttachedHost(this);

// If the portal specifies an origin, use that as the logical location of the component
Expand All @@ -75,30 +75,30 @@ export class PortalHostDirective extends BasePortalHost {
portal.injector || viewContainerRef.parentInjector);

this.setDisposeFn(() => ref.destroy());
return Promise.resolve(ref);
return ref;
}

/** Attach the given TemplatePortal to this PortlHost as an embedded View. */
attachTemplatePortal(portal: TemplatePortal): Promise<Map<string, any>> {
attachTemplatePortal(portal: TemplatePortal): Map<string, any> {
portal.setAttachedHost(this);

this._viewContainerRef.createEmbeddedView(portal.templateRef);
this.setDisposeFn(() => this._viewContainerRef.clear());

// TODO(jelbourn): return locals from view
return Promise.resolve(new Map<string, any>());
return new Map<string, any>();
}

/** Detatches the currently attached Portal (if there is one) and attaches the given Portal. */
private _replaceAttachedPortal(p: Portal<any>): void {
let maybeDetach = this.hasAttached() ? this.detach() : Promise.resolve(null);

maybeDetach.then(() => {
if (p) {
this.attach(p);
this._portal = p;
}
});
if (this.hasAttached()) {
this.detach();
}

if (p) {
this.attach(p);
this._portal = p;
}
}
}

Expand Down
10 changes: 2 additions & 8 deletions src/lib/core/portal/portal.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,10 +186,7 @@ describe('Portals', () => {
it('should attach and detach a component portal', fakeAsync(() => {
let portal = new ComponentPortal(PizzaMsg, someViewContainerRef);

let componentInstance: PizzaMsg;
portal.attach(host).then(ref => {
componentInstance = ref.instance;
});
let componentInstance: PizzaMsg = portal.attach(host).instance;

flushMicrotasks();

Expand All @@ -210,10 +207,7 @@ describe('Portals', () => {
let chocolateInjector = new ChocolateInjector(someInjector);
let portal = new ComponentPortal(PizzaMsg, someViewContainerRef, chocolateInjector);

let componentInstance: PizzaMsg;
portal.attach(host).then(ref => {
componentInstance = ref.instance;
});
let componentInstance: PizzaMsg = portal.attach(host).instance;

flushMicrotasks();
fixture.detectChanges();
Expand Down
Loading