Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit 6861142

Browse files
committed
fix(compile): Fix for nested multi-element directives
Fix an issue with nested multi-element directives that are siblings at the DOM level
1 parent 477ae0c commit 6861142

File tree

2 files changed

+37
-12
lines changed

2 files changed

+37
-12
lines changed

src/ng/compile.js

+21-12
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,7 @@ function $CompileProvider($provide) {
357357

358358
//================================
359359

360-
function compile($compileNodes, transcludeFn, maxPriority) {
360+
function compile($compileNodes, transcludeFn, maxPriority, limitMaxPriorityToFirstElement) {
361361
if (!($compileNodes instanceof jqLite)) {
362362
// jquery always rewraps, whereas we need to preserve the original selector so that we can modify it.
363363
$compileNodes = jqLite($compileNodes);
@@ -369,7 +369,8 @@ function $CompileProvider($provide) {
369369
$compileNodes[index] = jqLite(node).wrap('<span></span>').parent()[0];
370370
}
371371
});
372-
var compositeLinkFn = compileNodes($compileNodes, transcludeFn, $compileNodes, maxPriority);
372+
var compositeLinkFn = compileNodes($compileNodes, transcludeFn, $compileNodes, maxPriority,
373+
limitMaxPriorityToFirstElement);
373374
return function publicLinkFn(scope, cloneConnectFn){
374375
assertArg(scope, 'scope');
375376
// important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
@@ -418,17 +419,21 @@ function $CompileProvider($provide) {
418419
* rootElement must be set the jqLite collection of the compile root. This is
419420
* needed so that the jqLite collection items can be replaced with widgets.
420421
* @param {number=} max directive priority
422+
* @param {boolean=} if the max priority should only apply to the first element in the list.
423+
* A true value here will make the maxPriority only apply to the first element on the
424+
* list while the other elements on the list will not have a maxPriority set
421425
* @returns {?function} A composite linking function of all of the matched directives or null.
422426
*/
423-
function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority) {
427+
function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, limitMaxPriorityToFirstElement) {
424428
var linkFns = [],
425429
nodeLinkFn, childLinkFn, directives, attrs, linkFnFound;
426430

427431
for(var i = 0; i < nodeList.length; i++) {
428432
attrs = new Attributes();
429433

430434
// we must always refer to nodeList[i] since the nodes can be replaced underneath us.
431-
directives = collectDirectives(nodeList[i], [], attrs, maxPriority);
435+
directives = collectDirectives(nodeList[i], [], attrs,
436+
(limitMaxPriorityToFirstElement && i != 0) ? undefined : maxPriority);
432437

433438
nodeLinkFn = (directives.length)
434439
? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement)
@@ -646,13 +651,23 @@ function $CompileProvider($provide) {
646651
replaceWith($rootElement, jqLite($template[0]), compileNode);
647652
childTranscludeFn = compile($template, transcludeFn, terminalPriority);
648653
} else if (directiveValue == 'multi-element') {
649-
childTranscludeFn = compile(jqLite(extractMultiElementTransclude(compileNode, directiveName)),
650-
transcludeFn, terminalPriority);
654+
// We need to compile a clone of the elements, but at the same time we have to be sure that these elements are siblings
655+
var nestedContent = extractMultiElementTransclude(compileNode, directiveName);
656+
var cloneContent = jqLite('<div></div>');
657+
forEach(nestedContent, function(nestedElement) {
658+
cloneContent.append(JQLiteClone(nestedElement));
659+
});
660+
childTranscludeFn = compile(cloneContent.contents(), transcludeFn, terminalPriority, true);
651661
$template = jqLite(compileNode);
652662
$compileNode = templateAttrs.$$element =
653663
jqLite(document.createComment(' ' + directiveName + ': ' + templateAttrs[directiveName] + ' '));
654664
compileNode = $compileNode[0];
655665
replaceWith($rootElement, $template, compileNode);
666+
forEach(nestedContent.splice(1),
667+
function(toRemove) {
668+
replaceWith($rootElement, jqLite(toRemove), document.createComment(' placeholder '));
669+
}
670+
);
656671
} else {
657672
$template = jqLite(JQLiteClone(compileNode)).contents();
658673
$compileNode.html(''); // clear contents
@@ -754,12 +769,6 @@ function $CompileProvider($provide) {
754769
} while(count > 0 && cursor);
755770
if (count > 0) throw Error('Unmatched ' + transcludeStart + '.');
756771
if (count < 0) throw Error('Unexpected ' + transcludeEnd + '.');
757-
for (var j = 0; j < transcludeContent.length; ++j) {
758-
c = transcludeContent[j];
759-
transcludeContent[j] = JQLiteClone(transcludeContent[j]);
760-
// The first element will be replaced by a comment
761-
if (j != 0) jqLite(c).remove();
762-
}
763772
return transcludeContent;
764773
}
765774

test/ng/directive/ngRepeatStartSpec.js

+16
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,22 @@ describe('ngRepeatStart', function() {
428428
});
429429

430430

431+
it('should repeat over nested repeats at the same level', function() {
432+
element = $compile(
433+
'<dl>' +
434+
'<dt ng-repeat="item1 in array" ng-repeat-start>{{item1}}|</dt>' +
435+
'<dt ng-repeat="item2 in array2" ng-repeat-start>{{item2}}</dt>' +
436+
'<dt ng-repeat-end>X</dt>' +
437+
'<dt ng-repeat-end>Y</dt>' +
438+
'</dl>')(scope);
439+
scope.array = ['a', 'b'];
440+
scope.array2 = ['d', 'e'];
441+
scope.$digest();
442+
443+
expect(element.text()).toEqual('a|dXeXYb|dXeXY');
444+
});
445+
446+
431447
it('should ignore non-array element properties when iterating over an array', function() {
432448
element = $compile('<ul><li ng-repeat-start ng-repeat="item in array">{{item}}</li><li ng-repeat-end>|</li></ul>')(scope);
433449
scope.array = ['a', 'b', 'c'];

0 commit comments

Comments
 (0)