feat(dialog): support open with TemplateRef (#2910)

* feat(dialog): support open with TemplateRef

* add e2e test
This commit is contained in:
mmalerba
2017-02-09 10:32:07 -08:00
committed by tinayuangao
parent 3bcb7c3314
commit bf0f625ee3
7 changed files with 79 additions and 25 deletions
+6
View File
@@ -11,6 +11,12 @@ describe('dialog', () => {
expectToExist('md-dialog-container');
});
it('should open a template dialog', () => {
expectToExist('.my-template-dialog', false);
element(by.id('template')).click();
expectToExist('.my-template-dialog');
});
it('should close by clicking on the backdrop', () => {
element(by.id('default')).click();
+13 -2
View File
@@ -1,7 +1,14 @@
<h1>Dialog demo</h1>
<button md-raised-button color="primary" (click)="openJazz()" [disabled]="dialogRef">Open dialog</button>
<button md-raised-button color="accent" (click)="openContentElement()">Open dialog with content elements</button>
<button md-raised-button color="primary" (click)="openJazz()" [disabled]="dialogRef">
Open dialog
</button>
<button md-raised-button color="accent" (click)="openContentElement()">
Open dialog with content elements
</button>
<button md-raised-button color="accent" (click)="openTemplate()">
Open dialog with template content
</button>
<md-card class="demo-dialog-card">
<md-card-content>
@@ -59,3 +66,7 @@
</md-card>
<p>Last close result: {{lastCloseResult}}</p>
<template>
I'm a template dialog. I've been opened {{numTemplateOpens}} times!
</template>
+9 -1
View File
@@ -1,4 +1,4 @@
import {Component, Inject} from '@angular/core';
import {Component, Inject, ViewChild, TemplateRef} from '@angular/core';
import {DOCUMENT} from '@angular/platform-browser';
import {MdDialog, MdDialogRef, MdDialogConfig, MD_DIALOG_DATA} from '@angular/material';
@@ -27,6 +27,9 @@ export class DialogDemo {
message: 'Jazzy jazz jazz'
}
};
numTemplateOpens = 0;
@ViewChild(TemplateRef) template: TemplateRef<any>;
constructor(public dialog: MdDialog, @Inject(DOCUMENT) doc: any) {
// Possible useful example for the open and closeAll events.
@@ -55,6 +58,11 @@ export class DialogDemo {
let dialogRef = this.dialog.open(ContentElementDialog, this.config);
dialogRef.componentInstance.actionsAlignment = this.actionsAlignment;
}
openTemplate() {
this.numTemplateOpens++;
this.dialog.open(this.template, this.config);
}
}
+3
View File
@@ -1,2 +1,5 @@
<button id="default" (click)="openDefault()">DEFAULT</button>
<button id="disabled" (click)="openDisabled()">DISABLED</button>
<button id="template" (click)="openTemplate()">TEMPLATE</button>
<template><div class="my-template-dialog">my template dialog</div></template>
+7 -1
View File
@@ -1,4 +1,4 @@
import {Component} from '@angular/core';
import {Component, ViewChild, TemplateRef} from '@angular/core';
import {MdDialog, MdDialogRef, MdDialogConfig} from '@angular/material';
@Component({
@@ -9,6 +9,8 @@ import {MdDialog, MdDialogRef, MdDialogConfig} from '@angular/material';
export class DialogE2E {
dialogRef: MdDialogRef<TestDialog>;
@ViewChild(TemplateRef) templateRef: TemplateRef<any>;
constructor (private _dialog: MdDialog) { }
private _openDialog(config?: MdDialogConfig) {
@@ -28,6 +30,10 @@ export class DialogE2E {
disableClose: true
});
}
openTemplate() {
this.dialogRef = this._dialog.open(this.templateRef);
}
}
@Component({
+23 -8
View File
@@ -52,7 +52,7 @@ export class MdDialogContainer extends BasePortalHost implements OnDestroy {
}
/**
* Attach a portal as content to this dialog container.
* Attach a ComponentPortal as content to this dialog container.
* @param portal Portal to be attached as the dialog content.
*/
attachComponentPortal<T>(portal: ComponentPortal<T>): ComponentRef<T> {
@@ -61,7 +61,29 @@ export class MdDialogContainer extends BasePortalHost implements OnDestroy {
}
let attachResult = this._portalHost.attachComponentPortal(portal);
this._trapFocus();
return attachResult;
}
/**
* Attach a TemplatePortal as content to this dialog container.
* @param portal Portal to be attached as the dialog content.
*/
attachTemplatePortal(portal: TemplatePortal): Map<string, any> {
if (this._portalHost.hasAttached()) {
throw new MdDialogContentAlreadyAttachedError();
}
let attachedResult = this._portalHost.attachTemplatePortal(portal);
this._trapFocus();
return attachedResult;
}
/**
* Moves the focus inside the focus trap.
* @private
*/
private _trapFocus() {
// If were to attempt to focus immediately, then the content of the dialog would not yet be
// ready in instances where change detection has to run first. To deal with this, we simply
// wait for the microtask queue to be empty.
@@ -69,13 +91,6 @@ export class MdDialogContainer extends BasePortalHost implements OnDestroy {
this._elementFocusedBeforeDialogWasOpened = document.activeElement;
this._focusTrap.focusFirstTabbableElement();
});
return attachResult;
}
/** @docs-private */
attachTemplatePortal(portal: TemplatePortal): Map<string, any> {
throw Error('Not yet implemented');
}
/**
+18 -13
View File
@@ -1,17 +1,15 @@
import {Injector, ComponentRef, Injectable, Optional, SkipSelf} from '@angular/core';
import {Injector, ComponentRef, Injectable, Optional, SkipSelf, TemplateRef} from '@angular/core';
import {Observable} from 'rxjs/Observable';
import {Subject} from 'rxjs/Subject';
import {Overlay, OverlayRef, ComponentType, OverlayState, ComponentPortal} from '../core';
import {extendObject} from '../core/util/object-extend';
import {DialogInjector} from './dialog-injector';
import {MdDialogConfig} from './dialog-config';
import {MdDialogRef} from './dialog-ref';
import {MdDialogContainer} from './dialog-container';
import {TemplatePortal} from '../core/portal/portal';
// TODO(jelbourn): add support for opening with a TemplateRef
// TODO(jelbourn): animations
@@ -53,16 +51,19 @@ export class MdDialog {
/**
* Opens a modal dialog containing the given component.
* @param component Type of the component to load into the load.
* @param componentOrTemplateRef Type of the component to load into the dialog,
* or a TemplateRef to instantiate as the dialog content.
* @param config Extra configuration options.
* @returns Reference to the newly-opened dialog.
*/
open<T>(component: ComponentType<T>, config?: MdDialogConfig): MdDialogRef<T> {
open<T>(componentOrTemplateRef: ComponentType<T> | TemplateRef<T>,
config?: MdDialogConfig): MdDialogRef<T> {
config = _applyConfigDefaults(config);
let overlayRef = this._createOverlay(config);
let dialogContainer = this._attachDialogContainer(overlayRef, config);
let dialogRef = this._attachDialogContent(component, dialogContainer, overlayRef, config);
let dialogRef =
this._attachDialogContent(componentOrTemplateRef, dialogContainer, overlayRef, config);
this._openDialogs.push(dialogRef);
dialogRef.afterClosed().subscribe(() => this._removeOpenDialog(dialogRef));
@@ -114,14 +115,15 @@ export class MdDialog {
/**
* Attaches the user-provided component to the already-created MdDialogContainer.
* @param component The type of component being loaded into the dialog.
* @param componentOrTemplateRef The type of component being loaded into the dialog,
* or a TemplateRef to instantiate as the content.
* @param dialogContainer Reference to the wrapping MdDialogContainer.
* @param overlayRef Reference to the overlay in which the dialog resides.
* @param config The dialog configuration.
* @returns A promise resolving to the MdDialogRef that should be returned to the user.
*/
private _attachDialogContent<T>(
component: ComponentType<T>,
componentOrTemplateRef: ComponentType<T> | TemplateRef<T>,
dialogContainer: MdDialogContainer,
overlayRef: OverlayRef,
config?: MdDialogConfig): MdDialogRef<T> {
@@ -143,10 +145,13 @@ export class MdDialog {
let userInjector = config && config.viewContainerRef && config.viewContainerRef.injector;
let dialogInjector = new DialogInjector(userInjector || this._injector, dialogRef, config.data);
let contentPortal = new ComponentPortal(component, null, dialogInjector);
let contentRef = dialogContainer.attachComponentPortal(contentPortal);
dialogRef.componentInstance = contentRef.instance;
if (componentOrTemplateRef instanceof TemplateRef) {
dialogContainer.attachTemplatePortal(new TemplatePortal(componentOrTemplateRef, null));
} else {
let contentRef = dialogContainer.attachComponentPortal(
new ComponentPortal(componentOrTemplateRef, null, dialogInjector));
dialogRef.componentInstance = contentRef.instance;
}
return dialogRef;
}