185
185
* * `$scope` - Current scope associated with the element
186
186
* * `$element` - Current element
187
187
* * `$attrs` - Current attributes object for the element
188
- * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope.
189
- * The scope can be overridden by an optional first argument.
190
- * `function([scope], cloneLinkingFn)`.
188
+ * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope:
189
+ * `function([scope], cloneLinkingFn, futureParentNode)`.
190
+ * * `scope`: optional argument to override the scope.
191
+ * * `cloneLinkingFn`: optional argument to create clones of the original translcuded content.
192
+ * * `futureParentNode`:
193
+ * * defines the parent to which the `cloneLinkingFn` will add the cloned elements.
194
+ * * default: `$element.parent()` resp. `$element` for `transclude:'element'` resp. `transclude:true`.
195
+ * * only needed for transcludes that are allowed to contain non html elements (e.g. SVG elements)
196
+ * and when the `cloneLinkinFn` is passed,
197
+ * as those elements need to created and cloned in a special way when they are defined outside their
198
+ * usual containers (e.g. like `<svg>`).
199
+ * * See also the `directive.templateNamespace` property.
191
200
*
192
201
*
193
202
* #### `require`
265
274
* one. See the {@link guide/directive#creating-custom-directives_creating-directives_template-expanding-directive
266
275
* Directives Guide} for an example.
267
276
*
277
+ * There very few scenarios were element replacement is required for the application function,
278
+ * the main one being reusable custom components that are used within SVG contexts
279
+ * (because SVG doesn't work with custom elements in the DOM tree).
280
+ *
268
281
* #### `transclude`
269
282
* compile the content of the element and make it available to the directive.
270
283
* Typically used with {@link ng.directive:ngTransclude
359
372
* the directives to use the controllers as a communication channel.
360
373
*
361
374
* * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope.
362
- * The scope can be overridden by an optional first argument. This is the same as the `$transclude`
363
- * parameter of directive controllers.
364
- * `function([scope], cloneLinkingFn)`.
365
- *
375
+ * This is the same as the `$transclude`
376
+ * parameter of directive controllers, see there for details.
377
+ * `function([scope], cloneLinkingFn, futureParentNode)`.
366
378
*
367
379
* #### Pre-linking function
368
380
*
@@ -879,8 +891,18 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
879
891
compileNodes ( $compileNodes , transcludeFn , $compileNodes ,
880
892
maxPriority , ignoreDirective , previousCompileContext ) ;
881
893
safeAddClass ( $compileNodes , 'ng-scope' ) ;
882
- return function publicLinkFn ( scope , cloneConnectFn , transcludeControllers , parentBoundTranscludeFn ) {
894
+ var namespace = null ;
895
+ return function publicLinkFn ( scope , cloneConnectFn , transcludeControllers , parentBoundTranscludeFn , futureParentNode ) {
883
896
assertArg ( scope , 'scope' ) ;
897
+ if ( ! namespace ) {
898
+ namespace = detectNamespaceForChildElements ( futureParentNode ) ;
899
+ if ( namespace !== 'html' ) {
900
+ $compileNodes = jqLite (
901
+ wrapTemplate ( namespace , jqLite ( '<div>' ) . append ( $compileNodes ) . html ( ) )
902
+ ) ;
903
+ }
904
+ }
905
+
884
906
// important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
885
907
// and sometimes changes the structure of the DOM.
886
908
var $linkNode = cloneConnectFn
@@ -901,6 +923,16 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
901
923
} ;
902
924
}
903
925
926
+ function detectNamespaceForChildElements ( parentElement ) {
927
+ // TODO: Make this detect MathML as well...
928
+ var node = parentElement && parentElement [ 0 ] ;
929
+ if ( ! node ) {
930
+ return 'html' ;
931
+ } else {
932
+ return node . nodeName !== 'foreignObject' && node . toString ( ) . match ( / S V G / ) ? 'svg' : 'html' ;
933
+ }
934
+ }
935
+
904
936
function safeAddClass ( $element , className ) {
905
937
try {
906
938
$element . addClass ( className ) ;
@@ -1024,7 +1056,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
1024
1056
1025
1057
function createBoundTranscludeFn ( scope , transcludeFn , previousBoundTranscludeFn , elementTransclusion ) {
1026
1058
1027
- var boundTranscludeFn = function ( transcludedScope , cloneFn , controllers ) {
1059
+ var boundTranscludeFn = function ( transcludedScope , cloneFn , controllers , futureParentNode ) {
1028
1060
var scopeCreated = false ;
1029
1061
1030
1062
if ( ! transcludedScope ) {
@@ -1033,7 +1065,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
1033
1065
scopeCreated = true ;
1034
1066
}
1035
1067
1036
- var clone = transcludeFn ( transcludedScope , cloneFn , controllers , previousBoundTranscludeFn ) ;
1068
+ var clone = transcludeFn ( transcludedScope , cloneFn , controllers , previousBoundTranscludeFn , futureParentNode ) ;
1037
1069
if ( scopeCreated && ! elementTransclusion ) {
1038
1070
clone . on ( '$destroy' , function ( ) { transcludedScope . $destroy ( ) ; } ) ;
1039
1071
}
@@ -1645,20 +1677,24 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
1645
1677
}
1646
1678
1647
1679
// This is the function that is injected as `$transclude`.
1648
- function controllersBoundTransclude ( scope , cloneAttachFn ) {
1680
+ // Note: all arguments are optional!
1681
+ function controllersBoundTransclude ( scope , cloneAttachFn , futureParentNode ) {
1649
1682
var transcludeControllers ;
1650
1683
1651
- // no scope passed
1652
- if ( ! cloneAttachFn ) {
1684
+ // No scope passed in:
1685
+ if ( ! isScope ( scope ) ) {
1686
+ futureParentNode = cloneAttachFn ;
1653
1687
cloneAttachFn = scope ;
1654
1688
scope = undefined ;
1655
1689
}
1656
1690
1657
1691
if ( hasElementTranscludeDirective ) {
1658
1692
transcludeControllers = elementControllers ;
1659
1693
}
1660
-
1661
- return boundTranscludeFn ( scope , cloneAttachFn , transcludeControllers ) ;
1694
+ if ( ! futureParentNode ) {
1695
+ futureParentNode = hasElementTranscludeDirective ? $element . parent ( ) : $element ;
1696
+ }
1697
+ return boundTranscludeFn ( scope , cloneAttachFn , transcludeControllers , futureParentNode ) ;
1662
1698
}
1663
1699
}
1664
1700
}
0 commit comments