Skip to content

Commit bf0f625

Browse files
mmalerbatinayuangao
authored andcommitted
feat(dialog): support open with TemplateRef (#2910)
* feat(dialog): support open with TemplateRef * add e2e test
1 parent 3bcb7c3 commit bf0f625

File tree

7 files changed

+79
-25
lines changed

7 files changed

+79
-25
lines changed

e2e/components/dialog/dialog.e2e.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ describe('dialog', () => {
1111
expectToExist('md-dialog-container');
1212
});
1313

14+
it('should open a template dialog', () => {
15+
expectToExist('.my-template-dialog', false);
16+
element(by.id('template')).click();
17+
expectToExist('.my-template-dialog');
18+
});
19+
1420
it('should close by clicking on the backdrop', () => {
1521
element(by.id('default')).click();
1622

src/demo-app/dialog/dialog-demo.html

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
<h1>Dialog demo</h1>
22

3-
<button md-raised-button color="primary" (click)="openJazz()" [disabled]="dialogRef">Open dialog</button>
4-
<button md-raised-button color="accent" (click)="openContentElement()">Open dialog with content elements</button>
3+
<button md-raised-button color="primary" (click)="openJazz()" [disabled]="dialogRef">
4+
Open dialog
5+
</button>
6+
<button md-raised-button color="accent" (click)="openContentElement()">
7+
Open dialog with content elements
8+
</button>
9+
<button md-raised-button color="accent" (click)="openTemplate()">
10+
Open dialog with template content
11+
</button>
512

613
<md-card class="demo-dialog-card">
714
<md-card-content>
@@ -59,3 +66,7 @@ <h2>Other options</h2>
5966
</md-card>
6067

6168
<p>Last close result: {{lastCloseResult}}</p>
69+
70+
<template>
71+
I'm a template dialog. I've been opened {{numTemplateOpens}} times!
72+
</template>

src/demo-app/dialog/dialog-demo.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {Component, Inject} from '@angular/core';
1+
import {Component, Inject, ViewChild, TemplateRef} from '@angular/core';
22
import {DOCUMENT} from '@angular/platform-browser';
33
import {MdDialog, MdDialogRef, MdDialogConfig, MD_DIALOG_DATA} from '@angular/material';
44

@@ -27,6 +27,9 @@ export class DialogDemo {
2727
message: 'Jazzy jazz jazz'
2828
}
2929
};
30+
numTemplateOpens = 0;
31+
32+
@ViewChild(TemplateRef) template: TemplateRef<any>;
3033

3134
constructor(public dialog: MdDialog, @Inject(DOCUMENT) doc: any) {
3235
// Possible useful example for the open and closeAll events.
@@ -55,6 +58,11 @@ export class DialogDemo {
5558
let dialogRef = this.dialog.open(ContentElementDialog, this.config);
5659
dialogRef.componentInstance.actionsAlignment = this.actionsAlignment;
5760
}
61+
62+
openTemplate() {
63+
this.numTemplateOpens++;
64+
this.dialog.open(this.template, this.config);
65+
}
5866
}
5967

6068

src/e2e-app/dialog/dialog-e2e.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
11
<button id="default" (click)="openDefault()">DEFAULT</button>
22
<button id="disabled" (click)="openDisabled()">DISABLED</button>
3+
<button id="template" (click)="openTemplate()">TEMPLATE</button>
4+
5+
<template><div class="my-template-dialog">my template dialog</div></template>

src/e2e-app/dialog/dialog-e2e.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {Component} from '@angular/core';
1+
import {Component, ViewChild, TemplateRef} from '@angular/core';
22
import {MdDialog, MdDialogRef, MdDialogConfig} from '@angular/material';
33

44
@Component({
@@ -9,6 +9,8 @@ import {MdDialog, MdDialogRef, MdDialogConfig} from '@angular/material';
99
export class DialogE2E {
1010
dialogRef: MdDialogRef<TestDialog>;
1111

12+
@ViewChild(TemplateRef) templateRef: TemplateRef<any>;
13+
1214
constructor (private _dialog: MdDialog) { }
1315

1416
private _openDialog(config?: MdDialogConfig) {
@@ -28,6 +30,10 @@ export class DialogE2E {
2830
disableClose: true
2931
});
3032
}
33+
34+
openTemplate() {
35+
this.dialogRef = this._dialog.open(this.templateRef);
36+
}
3137
}
3238

3339
@Component({

src/lib/dialog/dialog-container.ts

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export class MdDialogContainer extends BasePortalHost implements OnDestroy {
5252
}
5353

5454
/**
55-
* Attach a portal as content to this dialog container.
55+
* Attach a ComponentPortal as content to this dialog container.
5656
* @param portal Portal to be attached as the dialog content.
5757
*/
5858
attachComponentPortal<T>(portal: ComponentPortal<T>): ComponentRef<T> {
@@ -61,21 +61,36 @@ export class MdDialogContainer extends BasePortalHost implements OnDestroy {
6161
}
6262

6363
let attachResult = this._portalHost.attachComponentPortal(portal);
64+
this._trapFocus();
65+
return attachResult;
66+
}
67+
68+
/**
69+
* Attach a TemplatePortal as content to this dialog container.
70+
* @param portal Portal to be attached as the dialog content.
71+
*/
72+
attachTemplatePortal(portal: TemplatePortal): Map<string, any> {
73+
if (this._portalHost.hasAttached()) {
74+
throw new MdDialogContentAlreadyAttachedError();
75+
}
6476

77+
let attachedResult = this._portalHost.attachTemplatePortal(portal);
78+
this._trapFocus();
79+
return attachedResult;
80+
}
81+
82+
/**
83+
* Moves the focus inside the focus trap.
84+
* @private
85+
*/
86+
private _trapFocus() {
6587
// If were to attempt to focus immediately, then the content of the dialog would not yet be
6688
// ready in instances where change detection has to run first. To deal with this, we simply
6789
// wait for the microtask queue to be empty.
6890
this._ngZone.onMicrotaskEmpty.first().subscribe(() => {
6991
this._elementFocusedBeforeDialogWasOpened = document.activeElement;
7092
this._focusTrap.focusFirstTabbableElement();
7193
});
72-
73-
return attachResult;
74-
}
75-
76-
/** @docs-private */
77-
attachTemplatePortal(portal: TemplatePortal): Map<string, any> {
78-
throw Error('Not yet implemented');
7994
}
8095

8196
/**

src/lib/dialog/dialog.ts

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
1-
import {Injector, ComponentRef, Injectable, Optional, SkipSelf} from '@angular/core';
1+
import {Injector, ComponentRef, Injectable, Optional, SkipSelf, TemplateRef} from '@angular/core';
22
import {Observable} from 'rxjs/Observable';
33
import {Subject} from 'rxjs/Subject';
4-
54
import {Overlay, OverlayRef, ComponentType, OverlayState, ComponentPortal} from '../core';
65
import {extendObject} from '../core/util/object-extend';
7-
86
import {DialogInjector} from './dialog-injector';
97
import {MdDialogConfig} from './dialog-config';
108
import {MdDialogRef} from './dialog-ref';
119
import {MdDialogContainer} from './dialog-container';
10+
import {TemplatePortal} from '../core/portal/portal';
1211

1312

14-
// TODO(jelbourn): add support for opening with a TemplateRef
1513
// TODO(jelbourn): animations
1614

1715

@@ -53,16 +51,19 @@ export class MdDialog {
5351

5452
/**
5553
* Opens a modal dialog containing the given component.
56-
* @param component Type of the component to load into the load.
54+
* @param componentOrTemplateRef Type of the component to load into the dialog,
55+
* or a TemplateRef to instantiate as the dialog content.
5756
* @param config Extra configuration options.
5857
* @returns Reference to the newly-opened dialog.
5958
*/
60-
open<T>(component: ComponentType<T>, config?: MdDialogConfig): MdDialogRef<T> {
59+
open<T>(componentOrTemplateRef: ComponentType<T> | TemplateRef<T>,
60+
config?: MdDialogConfig): MdDialogRef<T> {
6161
config = _applyConfigDefaults(config);
6262

6363
let overlayRef = this._createOverlay(config);
6464
let dialogContainer = this._attachDialogContainer(overlayRef, config);
65-
let dialogRef = this._attachDialogContent(component, dialogContainer, overlayRef, config);
65+
let dialogRef =
66+
this._attachDialogContent(componentOrTemplateRef, dialogContainer, overlayRef, config);
6667

6768
this._openDialogs.push(dialogRef);
6869
dialogRef.afterClosed().subscribe(() => this._removeOpenDialog(dialogRef));
@@ -114,14 +115,15 @@ export class MdDialog {
114115

115116
/**
116117
* Attaches the user-provided component to the already-created MdDialogContainer.
117-
* @param component The type of component being loaded into the dialog.
118+
* @param componentOrTemplateRef The type of component being loaded into the dialog,
119+
* or a TemplateRef to instantiate as the content.
118120
* @param dialogContainer Reference to the wrapping MdDialogContainer.
119121
* @param overlayRef Reference to the overlay in which the dialog resides.
120122
* @param config The dialog configuration.
121123
* @returns A promise resolving to the MdDialogRef that should be returned to the user.
122124
*/
123125
private _attachDialogContent<T>(
124-
component: ComponentType<T>,
126+
componentOrTemplateRef: ComponentType<T> | TemplateRef<T>,
125127
dialogContainer: MdDialogContainer,
126128
overlayRef: OverlayRef,
127129
config?: MdDialogConfig): MdDialogRef<T> {
@@ -143,10 +145,13 @@ export class MdDialog {
143145
let userInjector = config && config.viewContainerRef && config.viewContainerRef.injector;
144146
let dialogInjector = new DialogInjector(userInjector || this._injector, dialogRef, config.data);
145147

146-
let contentPortal = new ComponentPortal(component, null, dialogInjector);
147-
148-
let contentRef = dialogContainer.attachComponentPortal(contentPortal);
149-
dialogRef.componentInstance = contentRef.instance;
148+
if (componentOrTemplateRef instanceof TemplateRef) {
149+
dialogContainer.attachTemplatePortal(new TemplatePortal(componentOrTemplateRef, null));
150+
} else {
151+
let contentRef = dialogContainer.attachComponentPortal(
152+
new ComponentPortal(componentOrTemplateRef, null, dialogInjector));
153+
dialogRef.componentInstance = contentRef.instance;
154+
}
150155

151156
return dialogRef;
152157
}

0 commit comments

Comments
 (0)