|
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 that match `ng-transclude-select` selector to the `ng-transclude-select` element |
| 328 | + * in the directive template. |
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 |
|
@@ -2027,6 +2035,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
2027 | 2035 | }
|
2028 | 2036 | childLinkFn && childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn);
|
2029 | 2037 |
|
| 2038 | + if (thisLinkFn.multiTranscludeOnThisElement) multiTransclude($element[0], transcludeFn); |
| 2039 | + |
2030 | 2040 | // POSTLINKING
|
2031 | 2041 | for (i = postLinkFns.length - 1; i >= 0; i--) {
|
2032 | 2042 | linkFn = postLinkFns[i];
|
@@ -2128,6 +2138,48 @@ 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-selector. |
| 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 target, |
| 2153 | + selector, |
| 2154 | + selectedElements, |
| 2155 | + transcludeTargets = dirElement.querySelectorAll('[ng-transclude-select]'), |
| 2156 | + cloneWrapper = jqLite("<span></span>"); |
| 2157 | + cloneWrapper.append(clone); |
| 2158 | + |
| 2159 | + for (var i = 0, ii = transcludeTargets.length; i < ii; i++) { |
| 2160 | + target = jqLite(transcludeTargets[i]); |
| 2161 | + selector = target.attr('ng-transclude-select'); |
| 2162 | + selectedElements = cloneWrapper[0].querySelectorAll(selector); |
| 2163 | + if (selectedElements.length) target.append(selectedElements); |
| 2164 | + } |
| 2165 | + checkForTranscludeErr(cloneWrapper); |
| 2166 | + cloneWrapper.remove(); |
| 2167 | + } |
| 2168 | + |
| 2169 | + function checkForTranscludeErr(cloneWrapper) { |
| 2170 | + var orphanElement; |
| 2171 | + if (cloneWrapper.children().length) { |
| 2172 | + orphanElement = jqLite(cloneWrapper.children()[0]); |
| 2173 | + cloneWrapper.children().remove(); |
| 2174 | + throw $compileMinErr('invalidmulti', |
| 2175 | + 'Invalid transclusion. Element {0} does not match any known ng-transclude-select targets.', |
| 2176 | + startingTag(orphanElement)); |
| 2177 | + } |
| 2178 | + |
| 2179 | + } |
| 2180 | + } |
| 2181 | + |
| 2182 | + |
2131 | 2183 | /**
|
2132 | 2184 | * When the element is replaced with HTML template then the new attributes
|
2133 | 2185 | * on the template need to be merged with the existing attributes in the DOM.
|
|
0 commit comments