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 {bind, Injector} from 'angular2/di';
import {
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 {isPresent, Type} from 'angular2/src/facade/lang';
import {DOM} from 'angular2/src/dom/dom_adapter';
import {MouseEvent, KeyboardEvent} from 'angular2/src/facade/browser';
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): body scrolling is disabled while dialog is open.
// 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): Animate dialog out of / into opening element.
var _nextDialogId = 0;
/**
* Service for opening modal dialogs.
*/
@Injectable()
export class MdDialog {
componentLoader: DynamicComponentLoader;
domRenderer: DomRenderer;
@ -39,13 +42,12 @@ export class MdDialog {
* Opens a modal dialog.
* @param type The component to open.
* @param elementRef The logical location into which the component will be opened.
* @param parentInjector
* @param options
* @returns Promise for a reference to the dialog.
*/
open(
type: Type,
elementRef: ElementRef,
parentInjector: Injector,
options: MdDialogConfig = null): Promise<MdDialogRef> {
open(type: Type, elementRef: ElementRef, parentInjector: Injector,
options: MdDialogConfig = null): Promise<MdDialogRef> {
var config = isPresent(options) ? options : new MdDialogConfig();
// 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);
// First, load the MdDialogContainer, into which the given component will be loaded.
return this.componentLoader.loadIntoNewLocation(
MdDialogContainer, elementRef).then(containerRef => {
// TODO(tbosch): clean this up when we have custom renderers (https://github.com/angular/angular/issues/1807)
// TODO(jelbourn): Don't use direct DOM access. Need abstraction to create an element
// directly on the document body (also needed for web workers stuff).
// Create a DOM node to serve as a physical host element for the dialog.
var dialogElement = this.domRenderer.getHostElement(containerRef.hostView.render);
DOM.appendChild(DOM.query('body'), dialogElement);
return this.componentLoader.loadIntoNewLocation(MdDialogContainer, elementRef)
.then(containerRef => {
// TODO(tbosch): clean this up when we have custom renderers
// (https://github.com/angular/angular/issues/1807)
// TODO(jelbourn): Don't use direct DOM access. Need abstraction to create an element
// directly on the document body (also needed for web workers stuff).
// Create a DOM node to serve as a physical host element for the dialog.
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.
// Configure properties on the host element.
DOM.addClass(dialogElement, 'md-dialog');
DOM.setAttribute(dialogElement, 'tabindex', '0');
// TODO(jelbourn): Use hostProperties binding to set these once #1539 is fixed.
// Configure properties on the host element.
DOM.addClass(dialogElement, 'md-dialog');
DOM.setAttribute(dialogElement, 'tabindex', '0');
// TODO(jelbourn): Do this with hostProperties (or another rendering abstraction) once ready.
if (isPresent(config.width)) {
DOM.setStyle(dialogElement, 'width', config.width);
}
if (isPresent(config.height)) {
DOM.setStyle(dialogElement, 'height', config.height);
}
// TODO(jelbourn): Do this with hostProperties (or another rendering abstraction) once
// ready.
if (isPresent(config.width)) {
DOM.setStyle(dialogElement, 'width', config.width);
}
if (isPresent(config.height)) {
DOM.setStyle(dialogElement, 'height', config.height);
}
dialogRef.containerRef = containerRef;
dialogRef.containerRef = containerRef;
// Now load the given component into the MdDialogContainer.
return this.componentLoader.loadNextToExistingLocation(
type, containerRef.instance.contentRef, contentInjector).then(contentRef => {
// Now load the given component into the MdDialogContainer.
return this.componentLoader.loadNextToExistingLocation(
type, containerRef.instance.contentRef, contentInjector)
.then(contentRef => {
// 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
// opener.
dialogRef.contentRef = contentRef;
containerRef.instance.dialogRef = dialogRef;
// 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
// opener.
dialogRef.contentRef = contentRef;
containerRef.instance.dialogRef = dialogRef;
backdropRefPromise.then(backdropRef => {
dialogRef.whenClosed.then((_) => {
backdropRef.dispose();
});
backdropRefPromise.then(backdropRef => {
dialogRef.whenClosed.then((_) => { backdropRef.dispose(); });
});
return dialogRef;
});
});
return dialogRef;
});
});
}
/** Loads the dialog backdrop (transparent overlay over the rest of the page). */
_openBackdrop(elementRef:ElementRef, injector: Injector): Promise<ComponentRef> {
return this.componentLoader.loadIntoNewLocation(
MdBackdrop, elementRef, injector).then( (componentRef) => {
// TODO(tbosch): clean this up when we have custom renderers (https://github.com/angular/angular/issues/1807)
var backdropElement = this.domRenderer.getHostElement(componentRef.hostView.render);
DOM.addClass(backdropElement, 'md-backdrop');
DOM.appendChild(DOM.query('body'), backdropElement);
return componentRef;
});
_openBackdrop(elementRef: ElementRef, injector: Injector): Promise<ComponentRef> {
return this.componentLoader.loadIntoNewLocation(MdBackdrop, elementRef, injector)
.then((componentRef) => {
// TODO(tbosch): clean this up when we have custom renderers
// (https://github.com/angular/angular/issues/1807)
var backdropElement = this.domRenderer.getHostElement(componentRef.hostView.render);
DOM.addClass(backdropElement, 'md-backdrop');
DOM.appendChild(DOM.query('body'), backdropElement);
return componentRef;
});
}
alert(message: string, okMessage: string): Promise {
@ -163,8 +167,10 @@ export class MdDialogRef {
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 constructor of the very instance they are trying to get (which is much more easily accessed as `this`).
// The only time one could attempt to access this property before the value is set is if an
// 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.";
}
@ -203,13 +209,11 @@ export class MdDialogConfig {
*/
@Component({
selector: 'md-dialog-container',
hostListeners: {
'body:^keydown': 'documentKeypress($event)'
}
hostListeners: {'body:^keydown': 'documentKeypress($event)'},
})
@View({
templateUrl: 'angular2_material/src/components/dialog/dialog.html',
directives: [MdDialogContent]
directives: [FORWARD_REF(() => MdDialogContent)]
})
class MdDialogContainer {
// 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({
selector: 'md-backdrop',
hostListeners: {
'click': 'onClick()'
}
hostListeners: {'click': 'onClick()'},
})
@View({template: ''})
class MdBackdrop {
@ -256,15 +269,3 @@ class MdBackdrop {
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 {MdDialog, MdDialogRef, MdDialogConfig} from 'angular2_material/src/components/dialog/dialog'
import {bootstrap, ElementRef, ComponentRef, Component, View} from 'angular2/angular2';
import {
MdDialog,
MdDialogRef,
MdDialogConfig
} from 'angular2_material/src/components/dialog/dialog';
import {UrlResolver} from 'angular2/src/services/url_resolver';
import {commonDemoSetup, DemoUrlResolver} from '../demo_common';
import {bind, Injector} from 'angular2/di';
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({
selector: 'demo-app',
appInjector: [MdDialog]
appInjector: [MdDialog],
})
@View({
templateUrl: './demo_app.html',
directives: []
directives: [],
})
class DemoApp {
dialog: MdDialog;
@ -43,16 +42,16 @@ class DemoApp {
return;
}
this.dialog.open(SimpleDialogComponent,
this.elementRef, this.injector, this.dialogConfig).then(ref => {
this.dialogRef = ref;
ref.instance.numCoconuts = 777;
this.dialog.open(SimpleDialogComponent, this.elementRef, this.injector, this.dialogConfig)
.then(ref => {
this.dialogRef = ref;
ref.instance.numCoconuts = 777;
ref.whenClosed.then(result => {
this.dialogRef = null;
this.lastResult = result;
});
});
ref.whenClosed.then(result => {
this.dialogRef = null;
this.lastResult = result;
});
});
}
close() {
@ -62,7 +61,7 @@ class DemoApp {
@Component({
selector: 'simple-dialog',
properties: ['numCoconuts']
properties: ['numCoconuts'],
})
@View({
template: `
@ -70,7 +69,7 @@ class DemoApp {
<p>There are {{numCoconuts}} coconuts.</p>
<p>Return: <input (input)="updateValue($event)"></p>
<button type="button" (click)="done()">Done</button>
`
`,
})
class SimpleDialogComponent {
numCoconuts: number;
@ -95,9 +94,5 @@ class SimpleDialogComponent {
export function main() {
commonDemoSetup();
bootstrap(DemoApp, [
bind(UrlResolver).toValue(new DemoUrlResolver())
]);
bootstrap(DemoApp, [bind(UrlResolver).toValue(new DemoUrlResolver())]);
}