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

Commit fca21fe

Browse files
committed
feat(compile): Add transclude type "multi-element"
feat(ng-repeat): Make ng-repeat of transclude type multi-element Add a new transclude type multi-element to be able to handle transclusion that includes more elements than the one where the directive is present Make the changes to ngRepeat so it is of transclude type multi-element Closes #1891
1 parent 2d5297e commit fca21fe

File tree

3 files changed

+852
-5
lines changed

3 files changed

+852
-5
lines changed

src/ng/compile.js

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,14 @@ function $CompileProvider($provide) {
645645
compileNode = $compileNode[0];
646646
replaceWith($rootElement, jqLite($template[0]), compileNode);
647647
childTranscludeFn = compile($template, transcludeFn, terminalPriority);
648+
} else if (directiveValue == 'multi-element') {
649+
childTranscludeFn = compile(jqLite(extractMultiElementTransclude(compileNode, directiveName)),
650+
transcludeFn, terminalPriority);
651+
$template = jqLite(compileNode);
652+
$compileNode = templateAttrs.$$element =
653+
jqLite(document.createComment(' ' + directiveName + ': ' + templateAttrs[directiveName] + ' '));
654+
compileNode = $compileNode[0];
655+
replaceWith($rootElement, $template, compileNode);
648656
} else {
649657
$template = jqLite(JQLiteClone(compileNode)).contents();
650658
$compileNode.html(''); // clear contents
@@ -731,6 +739,61 @@ function $CompileProvider($provide) {
731739

732740
////////////////////
733741

742+
743+
function extractMultiElementTransclude(cursor, directiveName) {
744+
var transcludeContent = [],
745+
c, count = 0,
746+
transcludeStart = directiveName + 'Start',
747+
transcludeEnd = directiveName + 'End';
748+
749+
do {
750+
if (containsAttr(cursor, transcludeStart)) count++;
751+
if (containsAttr(cursor, transcludeEnd)) count--;
752+
transcludeContent.push(cursor);
753+
cursor = cursor.nextSibling;
754+
} while(count > 0 && cursor);
755+
if (count > 0) throw Error('Unmatched ' + transcludeStart + '.');
756+
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+
}
763+
return transcludeContent;
764+
}
765+
766+
767+
function containsAttr(node, attributeName) {
768+
var nodeType = node.nodeType,
769+
result = false;
770+
771+
switch(nodeType) {
772+
case 1:
773+
// iterate over the attributes
774+
for (var attr, name, nName, ngAttrName, nAttrs = node.attributes,
775+
j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
776+
attr = nAttrs[j];
777+
if (attr.specified) {
778+
name = attr.name;
779+
// support ngAttr attribute binding
780+
ngAttrName = directiveNormalize(name);
781+
if (NG_ATTR_BINDING.test(ngAttrName)) {
782+
name = ngAttrName.substr(6).toLowerCase();
783+
}
784+
nName = directiveNormalize(name.toLowerCase());
785+
if (nName == attributeName) {
786+
result = true;
787+
break;
788+
}
789+
}
790+
}
791+
break;
792+
}
793+
return result;
794+
}
795+
796+
734797
function addLinkFns(pre, post) {
735798
if (pre) {
736799
pre.require = directive.require;

src/ng/directive/ngRepeat.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@
145145
var ngRepeatDirective = ['$parse', '$animator', function($parse, $animator) {
146146
var NG_REMOVED = '$$NG_REMOVED';
147147
return {
148-
transclude: 'element',
148+
transclude: 'multi-element',
149149
priority: 1000,
150150
terminal: true,
151151
compile: function(element, attr, linker) {
@@ -258,7 +258,7 @@ var ngRepeatDirective = ['$parse', '$animator', function($parse, $animator) {
258258
if (lastBlockMap.hasOwnProperty(key)) {
259259
block = lastBlockMap[key];
260260
animate.leave(block.element);
261-
block.element[0][NG_REMOVED] = true;
261+
forEach(block.element, function(leavingElement) { leavingElement[NG_REMOVED] = true; });
262262
block.scope.$destroy();
263263
}
264264
}
@@ -274,7 +274,7 @@ var ngRepeatDirective = ['$parse', '$animator', function($parse, $animator) {
274274
// associated scope/element
275275
childScope = block.scope;
276276

277-
nextCursor = cursor[0];
277+
nextCursor = cursor[cursor.length - 1];
278278
do {
279279
nextCursor = nextCursor.nextSibling;
280280
} while(nextCursor && nextCursor[NG_REMOVED]);
@@ -284,7 +284,7 @@ var ngRepeatDirective = ['$parse', '$animator', function($parse, $animator) {
284284
cursor = block.element;
285285
} else {
286286
// existing item which got moved
287-
animate.move(block.element, null, cursor);
287+
animate.move(block.element, null, cursor.eq(-1));
288288
cursor = block.element;
289289
}
290290
} else {
@@ -301,7 +301,7 @@ var ngRepeatDirective = ['$parse', '$animator', function($parse, $animator) {
301301

302302
if (!block.element) {
303303
linker(childScope, function(clone) {
304-
animate.enter(clone, null, cursor);
304+
animate.enter(clone, null, cursor.eq(-1));
305305
cursor = clone;
306306
block.scope = childScope;
307307
block.element = clone;

0 commit comments

Comments
 (0)