@@ -200,6 +200,40 @@ function MdDialogDirective($$rAF, $mdTheming, $mdDialog) {
200
200
* })(angular);
201
201
* </hljs>
202
202
*
203
+ * ### Pre-Rendered Dialogs
204
+ * By using the `contentElement` option, it is possible to use an already existing element in the DOM.
205
+ *
206
+ * <hljs lang="js">
207
+ * $scope.showPrerenderedDialog = function() {
208
+ * $mdDialog.show({
209
+ * contentElement: '#myStaticDialog'
210
+ * parent: angular.element(document.body)
211
+ * });
212
+ * };
213
+ * </hljs>
214
+ *
215
+ * When using a string as value, `$mdDialog` will automatically query the DOM for the specified CSS selector.
216
+ *
217
+ * <hljs lang="html">
218
+ * <div style="visibility: hidden">
219
+ * <div class="md-dialog-container" id="myStaticDialog">
220
+ * <md-dialog>
221
+ * This is a pre-rendered dialog.
222
+ * </md-dialog>
223
+ * </div>
224
+ * </div>
225
+ * </hljs>
226
+ *
227
+ * **Notice**: It is important, to use the `.md-dialog-container` as the content element, otherwise the dialog
228
+ * will not show up.
229
+ *
230
+ * It also possible to use a DOM element for the `contentElement` option.
231
+ * - `contentElement: document.querySelector('#myStaticDialog')`
232
+ * - `contentElement: angular.element(TEMPLATE)`
233
+ *
234
+ * When using a `template` as content element, it will be not compiled upon open.
235
+ * This allows you to compile the element yourself and use it each time the dialog opens.
236
+ *
203
237
* ### JavaScript: promise API syntax, custom dialog template
204
238
* <hljs lang="js">
205
239
* (function(angular, undefined){
@@ -410,6 +444,11 @@ function MdDialogDirective($$rAF, $mdTheming, $mdDialog) {
410
444
* - `template` - `{string=}`: HTML template to show in the dialog. This **must** be trusted HTML
411
445
* with respect to Angular's [$sce service](https://docs.angularjs.org/api/ng/service/$sce).
412
446
* This template should **never** be constructed with any kind of user input or user data.
447
+ * - `contentElement` - `{string|Element}`: Instead of using a template, which will be compiled each time a
448
+ * dialog opens, you can also use a DOM element.<br/>
449
+ * * When specifying an element, which is present on the DOM, `$mdDialog` will temporary fetch the element into
450
+ * the dialog and restores it at the old DOM position upon close.
451
+ * * When specifying a string, the string be used as a CSS selector, to lookup for the element in the DOM.
413
452
* - `autoWrap` - `{boolean=}`: Whether or not to automatically wrap the template with a
414
453
* `<md-dialog>` tag if one is not provided. Defaults to true. Can be disabled if you provide a
415
454
* custom dialog directive.
@@ -491,7 +530,7 @@ function MdDialogProvider($$interimElementProvider) {
491
530
return $$interimElementProvider ( '$mdDialog' )
492
531
. setDefaults ( {
493
532
methods : [ 'disableParentScroll' , 'hasBackdrop' , 'clickOutsideToClose' , 'escapeToClose' ,
494
- 'targetEvent' , 'closeTo' , 'openFrom' , 'parent' , 'fullscreen' ] ,
533
+ 'targetEvent' , 'closeTo' , 'openFrom' , 'parent' , 'fullscreen' , 'contentElement' ] ,
495
534
options : dialogDefaultOptions
496
535
} )
497
536
. addPreset ( 'alert' , {
@@ -567,6 +606,7 @@ function MdDialogProvider($$interimElementProvider) {
567
606
clickOutsideToClose : false ,
568
607
escapeToClose : true ,
569
608
targetEvent : null ,
609
+ contentElement : null ,
570
610
closeTo : null ,
571
611
openFrom : null ,
572
612
focusOnOpen : true ,
@@ -613,6 +653,29 @@ function MdDialogProvider($$interimElementProvider) {
613
653
function onShow ( scope , element , options , controller ) {
614
654
angular . element ( $document [ 0 ] . body ) . addClass ( 'md-dialog-is-showing' ) ;
615
655
656
+ if ( options . contentElement ) {
657
+ var contentEl = options . contentElement ;
658
+
659
+ if ( angular . isString ( contentEl ) ) {
660
+ contentEl = document . querySelector ( contentEl ) ;
661
+ options . elementInsertionSibling = contentEl . nextElementSibling ;
662
+ options . elementInsertionParent = contentEl . parentNode ;
663
+ } else {
664
+ contentEl = contentEl [ 0 ] || contentEl ;
665
+ // When the element is not visible in the DOM, then we can treat is as same
666
+ // as a normal dialog would do. Removing it at close etc.
667
+ // ---
668
+ // When the element is visible in the DOM, then we restore it at close of the dialog.
669
+ if ( document . contains ( contentEl ) ) {
670
+ options . elementInsertionSibling = contentEl . nextElementSibling ;
671
+ options . elementInsertionParent = contentEl . parentNode ;
672
+ }
673
+ }
674
+
675
+ options . elementInsertionEntry = contentEl ;
676
+ element = angular . element ( contentEl ) ;
677
+ }
678
+
616
679
captureParentAndFromToElements ( options ) ;
617
680
configureAria ( element . find ( 'md-dialog' ) , options ) ;
618
681
showBackdrop ( scope , element , options ) ;
@@ -690,12 +753,37 @@ function MdDialogProvider($$interimElementProvider) {
690
753
return dialogPopOut ( element , options ) ;
691
754
}
692
755
756
+ function removeContentElement ( ) {
757
+ if ( ! options . contentElement ) return ;
758
+
759
+ options . reverseContainerStretch ( ) ;
760
+
761
+ if ( ! options . elementInsertionParent ) {
762
+ // When the contentElement has no parent, then it's a virtual DOM element, which should
763
+ // be removed at close, as same as normal templates inside of a dialog.
764
+ options . elementInsertionEntry . parentNode . removeChild ( options . elementInsertionEntry ) ;
765
+ } else if ( ! options . elementInsertionSibling ) {
766
+ // When the contentElement doesn't have any sibling, then it can be simply appended to the
767
+ // parent, because it plays no role, which index it had before.
768
+ options . elementInsertionParent . appendChild ( options . elementInsertionEntry ) ;
769
+ } else {
770
+ // When the contentElement has a sibling, which marks the previous position of the contentElement
771
+ // in the DOM, we insert it correctly before the sibling, to have the same index as before.
772
+ options . elementInsertionParent . insertBefore ( options . elementInsertionEntry , options . elementInsertionSibling ) ;
773
+ }
774
+ }
775
+
693
776
/**
694
777
* Detach the element
695
778
*/
696
779
function detachAndClean ( ) {
697
780
angular . element ( $document [ 0 ] . body ) . removeClass ( 'md-dialog-is-showing' ) ;
698
- element . remove ( ) ;
781
+ // Only remove the element, if it's not provided through the contentElement option.
782
+ if ( ! options . contentElement ) {
783
+ element . remove ( ) ;
784
+ } else {
785
+ removeContentElement ( ) ;
786
+ }
699
787
700
788
if ( ! options . $destroy ) options . origin . focus ( ) ;
701
789
}
@@ -764,7 +852,7 @@ function MdDialogProvider($$interimElementProvider) {
764
852
*/
765
853
function activateListeners ( element , options ) {
766
854
var window = angular . element ( $window ) ;
767
- var onWindowResize = $mdUtil . debounce ( function ( ) {
855
+ var onWindowResize = $mdUtil . debounce ( function ( ) {
768
856
stretchDialogContainerToViewport ( element , options ) ;
769
857
} , 60 ) ;
770
858
@@ -993,12 +1081,22 @@ function MdDialogProvider($$interimElementProvider) {
993
1081
var backdrop = options . backdrop ? $window . getComputedStyle ( options . backdrop [ 0 ] ) : null ;
994
1082
var height = backdrop ? Math . min ( $document [ 0 ] . body . clientHeight , Math . ceil ( Math . abs ( parseInt ( backdrop . height , 10 ) ) ) ) : 0 ;
995
1083
1084
+ var previousStyles = {
1085
+ top : container . css ( 'top' ) ,
1086
+ height : container . css ( 'height' )
1087
+ } ;
1088
+
996
1089
container . css ( {
997
1090
top : ( isFixed ? $mdUtil . scrollTop ( options . parent ) : 0 ) + 'px' ,
998
1091
height : height ? height + 'px' : '100%'
999
1092
} ) ;
1000
1093
1001
- return container ;
1094
+ return function ( ) {
1095
+ // Reverts the modified styles back to the previous values.
1096
+ // This is needed for contentElements, which should have the same styles after close
1097
+ // as before.
1098
+ container . css ( previousStyles ) ;
1099
+ } ;
1002
1100
}
1003
1101
1004
1102
/**
@@ -1007,7 +1105,7 @@ function MdDialogProvider($$interimElementProvider) {
1007
1105
function dialogPopIn ( container , options ) {
1008
1106
// Add the `md-dialog-container` to the DOM
1009
1107
options . parent . append ( container ) ;
1010
- stretchDialogContainerToViewport ( container , options ) ;
1108
+ options . reverseContainerStretch = stretchDialogContainerToViewport ( container , options ) ;
1011
1109
1012
1110
var dialogEl = container . find ( 'md-dialog' ) ;
1013
1111
var animator = $mdUtil . dom . animator ;
@@ -1023,7 +1121,7 @@ function MdDialogProvider($$interimElementProvider) {
1023
1121
return animator
1024
1122
. translate3d ( dialogEl , from , to , translateOptions )
1025
1123
. then ( function ( animateReversal ) {
1026
- // Build a reversal translate function synched to this translation...
1124
+ // Build a reversal translate function synced to this translation...
1027
1125
options . reverseAnimate = function ( ) {
1028
1126
delete options . reverseAnimate ;
1029
1127
@@ -1038,14 +1136,24 @@ function MdDialogProvider($$interimElementProvider) {
1038
1136
}
1039
1137
1040
1138
return animateReversal (
1041
- animator . toTransformCss (
1139
+ to = animator . toTransformCss (
1042
1140
// in case the origin element has moved or is hidden,
1043
1141
// let's recalculate the translateCSS
1044
1142
buildTranslateToOrigin ( dialogEl , options . origin )
1045
1143
)
1046
1144
) ;
1047
1145
1048
1146
} ;
1147
+
1148
+ // Builds a function, which clears the animations / transforms of the dialog element.
1149
+ // Required for contentElements, which should not have the the animation styling after
1150
+ // the dialog is closed.
1151
+ options . clearAnimate = function ( ) {
1152
+ delete options . clearAnimate ;
1153
+ return animator
1154
+ . translate3d ( dialogEl , to , animator . toTransformCss ( '' ) , { } ) ;
1155
+ } ;
1156
+
1049
1157
return true ;
1050
1158
} ) ;
1051
1159
}
@@ -1054,7 +1162,13 @@ function MdDialogProvider($$interimElementProvider) {
1054
1162
* Dialog close and pop-out animation
1055
1163
*/
1056
1164
function dialogPopOut ( container , options ) {
1057
- return options . reverseAnimate ( ) ;
1165
+ return options . reverseAnimate ( ) . then ( function ( ) {
1166
+ if ( options . contentElement ) {
1167
+ // When we use a contentElement, we want the element to be the same as before.
1168
+ // That means, that we have to clear all the animation properties, like transform.
1169
+ options . clearAnimate ( ) ;
1170
+ }
1171
+ } ) ;
1058
1172
}
1059
1173
1060
1174
/**
0 commit comments