mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
feat(dialog): support open with TemplateRef (#2910)
* feat(dialog): support open with TemplateRef * add e2e test
This commit is contained in:
@@ -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();
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user