chore(material): move dialog to TypeScript.

This commit is contained in:
Jeremy Elbourn 2015-05-29 16:27:54 -07:00
parent 4f3acdb004
commit c8947d77bf
2 changed files with 102 additions and 106 deletions

View File

@ -1,17 +1,21 @@
import {DynamicComponentLoader, ElementRef, ComponentRef, onDestroy, DomRenderer} from 'angular2/angular2'; import {
import {bind, Injector} from 'angular2/di'; Component,
Directive,
View,
Parent,
ElementRef,
DynamicComponentLoader,
ComponentRef,
DomRenderer
} from 'angular2/angular2';
import {bind, Injector, Injectable, FORWARD_REF} from 'angular2/di';
import {ObservableWrapper, Promise, PromiseWrapper} from 'angular2/src/facade/async'; import {ObservableWrapper, Promise, PromiseWrapper} from 'angular2/src/facade/async';
import {isPresent, Type} from 'angular2/src/facade/lang'; import {isPresent, Type} from 'angular2/src/facade/lang';
import {DOM} from 'angular2/src/dom/dom_adapter'; import {DOM} from 'angular2/src/dom/dom_adapter';
import {MouseEvent, KeyboardEvent} from 'angular2/src/facade/browser'; import {MouseEvent, KeyboardEvent} from 'angular2/src/facade/browser';
import {KEY_ESC} from 'angular2_material/src/core/constants'; import {KEY_ESC} from 'angular2_material/src/core/constants';
// TODO(radokirov): Once the application is transpiled by TS instead of Traceur,
// add those imports back into 'angular2/angular2';
import {Component, Directive} from 'angular2/src/core/annotations_impl/annotations';
import {Parent} from 'angular2/src/core/annotations_impl/visibility';
import {View} from 'angular2/src/core/annotations_impl/view';
// TODO(jelbourn): Opener of dialog can control where it is rendered. // TODO(jelbourn): Opener of dialog can control where it is rendered.
// TODO(jelbourn): body scrolling is disabled while dialog is open. // TODO(jelbourn): body scrolling is disabled while dialog is open.
// TODO(jelbourn): Don't manually construct and configure a DOM element. See #1402 // TODO(jelbourn): Don't manually construct and configure a DOM element. See #1402
@ -21,11 +25,10 @@ import {View} from 'angular2/src/core/annotations_impl/view';
// TODO(jelbourn): Pre-built `alert` and `confirm` dialogs. // TODO(jelbourn): Pre-built `alert` and `confirm` dialogs.
// TODO(jelbourn): Animate dialog out of / into opening element. // TODO(jelbourn): Animate dialog out of / into opening element.
var _nextDialogId = 0;
/** /**
* Service for opening modal dialogs. * Service for opening modal dialogs.
*/ */
@Injectable()
export class MdDialog { export class MdDialog {
componentLoader: DynamicComponentLoader; componentLoader: DynamicComponentLoader;
domRenderer: DomRenderer; domRenderer: DomRenderer;
@ -39,13 +42,12 @@ export class MdDialog {
* Opens a modal dialog. * Opens a modal dialog.
* @param type The component to open. * @param type The component to open.
* @param elementRef The logical location into which the component will be opened. * @param elementRef The logical location into which the component will be opened.
* @param parentInjector
* @param options
* @returns Promise for a reference to the dialog. * @returns Promise for a reference to the dialog.
*/ */
open( open(type: Type, elementRef: ElementRef, parentInjector: Injector,
type: Type, options: MdDialogConfig = null): Promise<MdDialogRef> {
elementRef: ElementRef,
parentInjector: Injector,
options: MdDialogConfig = null): Promise<MdDialogRef> {
var config = isPresent(options) ? options : new MdDialogConfig(); var config = isPresent(options) ? options : new MdDialogConfig();
// Create the dialogRef here so that it can be injected into the content component. // Create the dialogRef here so that it can be injected into the content component.
@ -57,61 +59,63 @@ export class MdDialog {
var backdropRefPromise = this._openBackdrop(elementRef, contentInjector); var backdropRefPromise = this._openBackdrop(elementRef, contentInjector);
// First, load the MdDialogContainer, into which the given component will be loaded. // First, load the MdDialogContainer, into which the given component will be loaded.
return this.componentLoader.loadIntoNewLocation( return this.componentLoader.loadIntoNewLocation(MdDialogContainer, elementRef)
MdDialogContainer, elementRef).then(containerRef => { .then(containerRef => {
// TODO(tbosch): clean this up when we have custom renderers (https://github.com/angular/angular/issues/1807) // TODO(tbosch): clean this up when we have custom renderers
// TODO(jelbourn): Don't use direct DOM access. Need abstraction to create an element // (https://github.com/angular/angular/issues/1807)
// directly on the document body (also needed for web workers stuff). // TODO(jelbourn): Don't use direct DOM access. Need abstraction to create an element
// Create a DOM node to serve as a physical host element for the dialog. // directly on the document body (also needed for web workers stuff).
var dialogElement = this.domRenderer.getHostElement(containerRef.hostView.render); // Create a DOM node to serve as a physical host element for the dialog.
DOM.appendChild(DOM.query('body'), dialogElement); var dialogElement = this.domRenderer.getHostElement(containerRef.hostView.render);
DOM.appendChild(DOM.query('body'), dialogElement);
// TODO(jelbourn): Use hostProperties binding to set these once #1539 is fixed. // TODO(jelbourn): Use hostProperties binding to set these once #1539 is fixed.
// Configure properties on the host element. // Configure properties on the host element.
DOM.addClass(dialogElement, 'md-dialog'); DOM.addClass(dialogElement, 'md-dialog');
DOM.setAttribute(dialogElement, 'tabindex', '0'); DOM.setAttribute(dialogElement, 'tabindex', '0');
// TODO(jelbourn): Do this with hostProperties (or another rendering abstraction) once ready. // TODO(jelbourn): Do this with hostProperties (or another rendering abstraction) once
if (isPresent(config.width)) { // ready.
DOM.setStyle(dialogElement, 'width', config.width); if (isPresent(config.width)) {
} DOM.setStyle(dialogElement, 'width', config.width);
if (isPresent(config.height)) { }
DOM.setStyle(dialogElement, 'height', config.height); if (isPresent(config.height)) {
} DOM.setStyle(dialogElement, 'height', config.height);
}
dialogRef.containerRef = containerRef; dialogRef.containerRef = containerRef;
// Now load the given component into the MdDialogContainer. // Now load the given component into the MdDialogContainer.
return this.componentLoader.loadNextToExistingLocation( return this.componentLoader.loadNextToExistingLocation(
type, containerRef.instance.contentRef, contentInjector).then(contentRef => { type, containerRef.instance.contentRef, contentInjector)
.then(contentRef => {
// Wrap both component refs for the container and the content so that we can return // Wrap both component refs for the container and the content so that we can return
// the `instance` of the content but the dispose method of the container back to the // the `instance` of the content but the dispose method of the container back to the
// opener. // opener.
dialogRef.contentRef = contentRef; dialogRef.contentRef = contentRef;
containerRef.instance.dialogRef = dialogRef; containerRef.instance.dialogRef = dialogRef;
backdropRefPromise.then(backdropRef => { backdropRefPromise.then(backdropRef => {
dialogRef.whenClosed.then((_) => { dialogRef.whenClosed.then((_) => { backdropRef.dispose(); });
backdropRef.dispose(); });
});
return dialogRef;
});
}); });
return dialogRef;
});
});
} }
/** Loads the dialog backdrop (transparent overlay over the rest of the page). */ /** Loads the dialog backdrop (transparent overlay over the rest of the page). */
_openBackdrop(elementRef:ElementRef, injector: Injector): Promise<ComponentRef> { _openBackdrop(elementRef: ElementRef, injector: Injector): Promise<ComponentRef> {
return this.componentLoader.loadIntoNewLocation( return this.componentLoader.loadIntoNewLocation(MdBackdrop, elementRef, injector)
MdBackdrop, elementRef, injector).then( (componentRef) => { .then((componentRef) => {
// TODO(tbosch): clean this up when we have custom renderers (https://github.com/angular/angular/issues/1807) // TODO(tbosch): clean this up when we have custom renderers
var backdropElement = this.domRenderer.getHostElement(componentRef.hostView.render); // (https://github.com/angular/angular/issues/1807)
DOM.addClass(backdropElement, 'md-backdrop'); var backdropElement = this.domRenderer.getHostElement(componentRef.hostView.render);
DOM.appendChild(DOM.query('body'), backdropElement); DOM.addClass(backdropElement, 'md-backdrop');
return componentRef; DOM.appendChild(DOM.query('body'), backdropElement);
}); return componentRef;
});
} }
alert(message: string, okMessage: string): Promise { alert(message: string, okMessage: string): Promise {
@ -163,8 +167,10 @@ export class MdDialogRef {
return this._contentRef.instance; return this._contentRef.instance;
} }
// The only time one could attempt to access this property before the value is set is if an access occurs during // The only time one could attempt to access this property before the value is set is if an
// the constructor of the very instance they are trying to get (which is much more easily accessed as `this`). // access occurs during
// the constructor of the very instance they are trying to get (which is much more easily
// accessed as `this`).
throw "Cannot access dialog component instance *from* that component's constructor."; throw "Cannot access dialog component instance *from* that component's constructor.";
} }
@ -203,13 +209,11 @@ export class MdDialogConfig {
*/ */
@Component({ @Component({
selector: 'md-dialog-container', selector: 'md-dialog-container',
hostListeners: { hostListeners: {'body:^keydown': 'documentKeypress($event)'},
'body:^keydown': 'documentKeypress($event)'
}
}) })
@View({ @View({
templateUrl: 'angular2_material/src/components/dialog/dialog.html', templateUrl: 'angular2_material/src/components/dialog/dialog.html',
directives: [MdDialogContent] directives: [FORWARD_REF(() => MdDialogContent)]
}) })
class MdDialogContainer { class MdDialogContainer {
// Ref to the dialog content. Used by the DynamicComponentLoader to load the dialog content. // Ref to the dialog content. Used by the DynamicComponentLoader to load the dialog content.
@ -234,13 +238,22 @@ class MdDialogContainer {
} }
} }
/**
* Simple decorator used only to communicate an ElementRef to the parent MdDialogContainer as the
* location
* for where the dialog content will be loaded.
*/
@Directive({selector: 'md-dialog-content'})
class MdDialogContent {
constructor(@Parent() dialogContainer: MdDialogContainer, elementRef: ElementRef) {
dialogContainer.contentRef = elementRef;
}
}
/** Component for the dialog "backdrop", a transparent overlay over the rest of the page. */ /** Component for the dialog "backdrop", a transparent overlay over the rest of the page. */
@Component({ @Component({
selector: 'md-backdrop', selector: 'md-backdrop',
hostListeners: { hostListeners: {'click': 'onClick()'},
'click': 'onClick()'
}
}) })
@View({template: ''}) @View({template: ''})
class MdBackdrop { class MdBackdrop {
@ -256,15 +269,3 @@ class MdBackdrop {
this.dialogRef.close(); this.dialogRef.close();
} }
} }
/**
* Simple decorator used only to communicate an ElementRef to the parent MdDialogContainer as the location
* for where the dialog content will be loaded.
*/
@Directive({selector: 'md-dialog-content'})
class MdDialogContent {
constructor(@Parent() dialogContainer: MdDialogContainer, elementRef: ElementRef) {
dialogContainer.contentRef = elementRef;
}
}

View File

@ -1,23 +1,22 @@
import {bootstrap, ElementRef, ComponentRef} from 'angular2/angular2'; import {bootstrap, ElementRef, ComponentRef, Component, View} from 'angular2/angular2';
import {MdDialog, MdDialogRef, MdDialogConfig} from 'angular2_material/src/components/dialog/dialog' import {
MdDialog,
MdDialogRef,
MdDialogConfig
} from 'angular2_material/src/components/dialog/dialog';
import {UrlResolver} from 'angular2/src/services/url_resolver'; import {UrlResolver} from 'angular2/src/services/url_resolver';
import {commonDemoSetup, DemoUrlResolver} from '../demo_common'; import {commonDemoSetup, DemoUrlResolver} from '../demo_common';
import {bind, Injector} from 'angular2/di'; import {bind, Injector} from 'angular2/di';
import {isPresent} from 'angular2/src/facade/lang'; import {isPresent} from 'angular2/src/facade/lang';
// TODO(radokirov): Once the application is transpiled by TS instead of Traceur,
// add those imports back into 'angular2/angular2';
import {Component, Directive} from 'angular2/src/core/annotations_impl/annotations';
import {View} from 'angular2/src/core/annotations_impl/view';
@Component({ @Component({
selector: 'demo-app', selector: 'demo-app',
appInjector: [MdDialog] appInjector: [MdDialog],
}) })
@View({ @View({
templateUrl: './demo_app.html', templateUrl: './demo_app.html',
directives: [] directives: [],
}) })
class DemoApp { class DemoApp {
dialog: MdDialog; dialog: MdDialog;
@ -43,16 +42,16 @@ class DemoApp {
return; return;
} }
this.dialog.open(SimpleDialogComponent, this.dialog.open(SimpleDialogComponent, this.elementRef, this.injector, this.dialogConfig)
this.elementRef, this.injector, this.dialogConfig).then(ref => { .then(ref => {
this.dialogRef = ref; this.dialogRef = ref;
ref.instance.numCoconuts = 777; ref.instance.numCoconuts = 777;
ref.whenClosed.then(result => { ref.whenClosed.then(result => {
this.dialogRef = null; this.dialogRef = null;
this.lastResult = result; this.lastResult = result;
}); });
}); });
} }
close() { close() {
@ -62,7 +61,7 @@ class DemoApp {
@Component({ @Component({
selector: 'simple-dialog', selector: 'simple-dialog',
properties: ['numCoconuts'] properties: ['numCoconuts'],
}) })
@View({ @View({
template: ` template: `
@ -70,7 +69,7 @@ class DemoApp {
<p>There are {{numCoconuts}} coconuts.</p> <p>There are {{numCoconuts}} coconuts.</p>
<p>Return: <input (input)="updateValue($event)"></p> <p>Return: <input (input)="updateValue($event)"></p>
<button type="button" (click)="done()">Done</button> <button type="button" (click)="done()">Done</button>
` `,
}) })
class SimpleDialogComponent { class SimpleDialogComponent {
numCoconuts: number; numCoconuts: number;
@ -95,9 +94,5 @@ class SimpleDialogComponent {
export function main() { export function main() {
commonDemoSetup(); commonDemoSetup();
bootstrap(DemoApp, [ bootstrap(DemoApp, [bind(UrlResolver).toValue(new DemoUrlResolver())]);
bind(UrlResolver).toValue(new DemoUrlResolver())
]);
} }