|
316 | 316 | * The contents are compiled and provided to the directive as a **transclusion function**. See the
|
317 | 317 | * {@link $compile#transclusion Transclusion} section below.
|
318 | 318 | *
|
319 |
| - * There are two kinds of transclusion depending upon whether you want to transclude just the contents of the |
320 |
| - * directive's element or the entire element: |
| 319 | + * There are three kinds of transclusion depending upon whether you want to transclude just the contents of the |
| 320 | + * directive's element, the entire element, or transclude to multiple points in the directive. |
321 | 321 | *
|
322 | 322 | * * `true` - transclude the content (i.e. the child nodes) of the directive's element.
|
323 | 323 | * * `'element'` - transclude the whole of the directive's element including any directives on this
|
324 | 324 | * element that defined at a lower priority than this directive. When used, the `template`
|
325 | 325 | * property is ignored.
|
| 326 | + * * `'multi'` - allows transclusion into multiple points of the directive's template. When used, automatically |
| 327 | + * appends any transcluded elements with `ng-transclude-to` attributes to elements in the directive's template with |
| 328 | + * corresponding `ng-transclude-id` attributes. |
326 | 329 | *
|
327 | 330 | *
|
328 | 331 | * #### `compile`
|
@@ -1645,6 +1648,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
1645 | 1648 | templateDirective = previousCompileContext.templateDirective,
|
1646 | 1649 | nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective,
|
1647 | 1650 | hasTranscludeDirective = false,
|
| 1651 | + hasMultiTranscludeDirective = false, |
1648 | 1652 | hasTemplate = false,
|
1649 | 1653 | hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective,
|
1650 | 1654 | $compileNode = templateAttrs.$$element = jqLite(compileNode),
|
@@ -1737,6 +1741,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
1737 | 1741 | nonTlbTranscludeDirective: nonTlbTranscludeDirective
|
1738 | 1742 | });
|
1739 | 1743 | } else {
|
| 1744 | + if (directiveValue == 'multi') { |
| 1745 | + hasMultiTranscludeDirective = true; |
| 1746 | + } |
1740 | 1747 | $template = jqLite(jqLiteClone(compileNode)).contents();
|
1741 | 1748 | $compileNode.empty(); // clear contents
|
1742 | 1749 | childTranscludeFn = compile($template, transcludeFn);
|
@@ -1833,6 +1840,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
1833 | 1840 | nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true;
|
1834 | 1841 | nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective;
|
1835 | 1842 | nodeLinkFn.elementTranscludeOnThisElement = hasElementTranscludeDirective;
|
| 1843 | + nodeLinkFn.multiTranscludeOnThisElement = hasMultiTranscludeDirective; |
1836 | 1844 | nodeLinkFn.templateOnThisElement = hasTemplate;
|
1837 | 1845 | nodeLinkFn.transclude = childTranscludeFn;
|
1838 | 1846 |
|
@@ -2039,6 +2047,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
2039 | 2047 | );
|
2040 | 2048 | }
|
2041 | 2049 |
|
| 2050 | + if (thisLinkFn.multiTranscludeOnThisElement) multiTransclude($element, transcludeFn); |
| 2051 | + |
2042 | 2052 | // This is the function that is injected as `$transclude`.
|
2043 | 2053 | // Note: all arguments are optional!
|
2044 | 2054 | function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement) {
|
@@ -2128,6 +2138,39 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
2128 | 2138 | return false;
|
2129 | 2139 | }
|
2130 | 2140 |
|
| 2141 | + /** |
| 2142 | + * Matches elements in the transcluded content to elements in the directive template by ng-transclude-id. |
| 2143 | + * Used in directives that set transclude to 'multi'. |
| 2144 | + * |
| 2145 | + * @param {jqLite} dirElement Main directive element |
| 2146 | + * @param {function()} transcludeFn Transclusion function for the directive |
| 2147 | + **/ |
| 2148 | + function multiTransclude(dirElement, transcludeFn) { |
| 2149 | + transcludeFn(transcludeCallback); |
| 2150 | + |
| 2151 | + function transcludeCallback(clone) { |
| 2152 | + var cloneElement, targetID, target; |
| 2153 | + |
| 2154 | + for (var i = 0, ii = clone.length; i < ii; i++) { |
| 2155 | + cloneElement = clone[i]; |
| 2156 | + if (cloneElement.nodeType === NODE_TYPE_TEXT) continue; |
| 2157 | + targetID = cloneElement.getAttribute('ng-transclude-to'); |
| 2158 | + target = jqLite(dirElement[0].querySelector('[ng-transclude-id="' + targetID + '"]')); |
| 2159 | + checkForTranscludeErr(target, cloneElement, targetID); |
| 2160 | + target.append(cloneElement); |
| 2161 | + } |
| 2162 | + } |
| 2163 | + } |
| 2164 | + |
| 2165 | + function checkForTranscludeErr(target, cloneElement, targetID) { |
| 2166 | + if (!target.length) { |
| 2167 | + jqLite(cloneElement).remove(); |
| 2168 | + throw $compileMinErr('notranscludetarget', |
| 2169 | + 'Target {0} is not valid. Please specify a proper ng-transclude-to attribute for <{1}>{2}</{1}>.', |
| 2170 | + targetID, cloneElement.tagName.toLowerCase(), cloneElement.innerHTML); |
| 2171 | + } |
| 2172 | + } |
| 2173 | + |
2131 | 2174 | /**
|
2132 | 2175 | * When the element is replaced with HTML template then the new attributes
|
2133 | 2176 | * on the template need to be merged with the existing attributes in the DOM.
|
|
0 commit comments