237
237
* as those elements need to created and cloned in a special way when they are defined outside their
238
238
* usual containers (e.g. like `<svg>`).
239
239
* * See also the `directive.templateNamespace` property.
240
- *
240
+ * The `$transclude` function has a property called `$slots`, which is a hash of slot names to slot transclusion
241
+ * functions. If a slot was declared but not filled its value on the `$slots` object will be `null`.
241
242
*
242
243
* #### `require`
243
244
* Require another directive and inject its controller as the fourth argument to the linking function. The
337
338
* The contents are compiled and provided to the directive as a **transclusion function**. See the
338
339
* {@link $compile#transclusion Transclusion} section below.
339
340
*
340
- * There are two kinds of transclusion depending upon whether you want to transclude just the contents of the
341
- * directive's element or the entire element:
341
+ * There are three kinds of transclusion depending upon whether you want to transclude just the contents of the
342
+ * directive's element, the entire element or parts of the element:
342
343
*
343
344
* * `true` - transclude the content (i.e. the child nodes) of the directive's element.
344
345
* * `'element'` - transclude the whole of the directive's element including any directives on this
345
346
* element that defined at a lower priority than this directive. When used, the `template`
346
347
* property is ignored.
348
+ * * **`{...}` (an object hash):** - map elements of the content onto transclusion "slots" in the template.
349
+ * See {@link ngTransclude} for more information.
350
+ *
351
+ * Mult-slot transclusion is declared by providing an object for the `transclude` property.
352
+ * This object is a map where the keys are the canonical name of HTML elements to match in the transcluded HTML,
353
+ * and the values are the names of the slots. If the name is prefixed with a `?` then that slot is optional.
347
354
*
355
+ * The slots are made available as `$transclude.$slots` on the transclude function that is passed to the
356
+ * linking functions as the fifth parameter, and can be injected into the directive controller.
348
357
*
349
358
* #### `compile`
350
359
*
@@ -1511,7 +1520,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
1511
1520
// so that they are available inside the `controllersBoundTransclude` function
1512
1521
var boundSlots = boundTranscludeFn . $$slots = createMap ( ) ;
1513
1522
for ( var slotName in transcludeFn . $$slots ) {
1514
- boundSlots [ slotName ] = createBoundTranscludeFn ( scope , transcludeFn . $$slots [ slotName ] , previousBoundTranscludeFn ) ;
1523
+ if ( transcludeFn . $$slots [ slotName ] ) {
1524
+ boundSlots [ slotName ] = createBoundTranscludeFn ( scope , transcludeFn . $$slots [ slotName ] , previousBoundTranscludeFn ) ;
1525
+ } else {
1526
+ boundSlots [ slotName ] = null ;
1527
+ }
1515
1528
}
1516
1529
1517
1530
return boundTranscludeFn ;
@@ -1870,7 +1883,10 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
1870
1883
var optional = ( slotName . charAt ( 0 ) === '?' ) ;
1871
1884
slotName = optional ? slotName . substring ( 1 ) : slotName ;
1872
1885
slotNames [ key ] = slotName ;
1873
- slots [ slotName ] = [ ] ;
1886
+ // We explicitly assign `null` since this implies that a slot was defined but not filled.
1887
+ // Later when calling boundTransclusion functions with a slot name we only error if the
1888
+ // slot is `undefined`
1889
+ slots [ slotName ] = null ;
1874
1890
// filledSlots contains `true` for all slots that are either optional or have been
1875
1891
// filled. This is used to check that we have not missed any required slots
1876
1892
filledSlots [ slotName ] = optional ;
@@ -1881,6 +1897,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
1881
1897
var slotName = slotNames [ directiveNormalize ( nodeName_ ( node ) ) ] ;
1882
1898
if ( slotName ) {
1883
1899
filledSlots [ slotName ] = true ;
1900
+ slots [ slotName ] = slots [ slotName ] || [ ] ;
1884
1901
slots [ slotName ] . push ( node ) ;
1885
1902
} else {
1886
1903
$template . push ( node ) ;
@@ -1894,9 +1911,12 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
1894
1911
}
1895
1912
} ) ;
1896
1913
1897
- forEach ( Object . keys ( slots ) , function ( slotName ) {
1898
- slots [ slotName ] = compilationGenerator ( mightHaveMultipleTransclusionError , slots [ slotName ] , transcludeFn ) ;
1899
- } ) ;
1914
+ for ( var slotName in slots ) {
1915
+ if ( slots [ slotName ] ) {
1916
+ // Only define a transclusion function if the slot was filled
1917
+ slots [ slotName ] = compilationGenerator ( mightHaveMultipleTransclusionError , slots [ slotName ] , transcludeFn ) ;
1918
+ }
1919
+ }
1900
1920
}
1901
1921
1902
1922
$compileNode . empty ( ) ; // clear contents
@@ -2125,6 +2145,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
2125
2145
// is later passed as `parentBoundTranscludeFn` to `publicLinkFn`
2126
2146
transcludeFn = controllersBoundTransclude ;
2127
2147
transcludeFn . $$boundTransclude = boundTranscludeFn ;
2148
+ // expose the slots on the `$transclude` function
2149
+ transcludeFn . $slots = boundTranscludeFn . $$slots ;
2128
2150
}
2129
2151
2130
2152
if ( controllerDirectives ) {
@@ -2221,16 +2243,22 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
2221
2243
futureParentElement = hasElementTranscludeDirective ? $element . parent ( ) : $element ;
2222
2244
}
2223
2245
if ( slotName ) {
2246
+ // slotTranscludeFn can be one of three things:
2247
+ // * a transclude function - a filled slot
2248
+ // * `null` - an optional slot that was not filled
2249
+ // * `undefined` - a slot that was not declared (i.e. invalid)
2224
2250
var slotTranscludeFn = boundTranscludeFn . $$slots [ slotName ] ;
2225
- if ( ! slotTranscludeFn ) {
2251
+ if ( slotTranscludeFn ) {
2252
+ return slotTranscludeFn ( scope , cloneAttachFn , transcludeControllers , futureParentElement , scopeToChild ) ;
2253
+ } else if ( isUndefined ( slotTranscludeFn ) ) {
2226
2254
throw $compileMinErr ( 'noslot' ,
2227
2255
'No parent directive that requires a transclusion with slot name "{0}". ' +
2228
2256
'Element: {1}' ,
2229
2257
slotName , startingTag ( $element ) ) ;
2230
2258
}
2231
- return slotTranscludeFn ( scope , cloneAttachFn , transcludeControllers , futureParentElement , scopeToChild ) ;
2259
+ } else {
2260
+ return boundTranscludeFn ( scope , cloneAttachFn , transcludeControllers , futureParentElement , scopeToChild ) ;
2232
2261
}
2233
- return boundTranscludeFn ( scope , cloneAttachFn , transcludeControllers , futureParentElement , scopeToChild ) ;
2234
2262
}
2235
2263
}
2236
2264
}
0 commit comments