Skip to content

Commit 5a2db25

Browse files
committed
fix($compile): support transcluding multi-element directives
Previously, transcluding multi-element directives (e.g. `foo-start`/`foo-end`) was not supported on elements with multi-slot transclusion (a `uterdir` error was thrown). This commit fixes it by putting the transcluded nodes into a DocumentFragment, where they can be traversed via `.nextSibling`. Fixes angular#15554
1 parent a24777a commit 5a2db25

File tree

2 files changed

+51
-5
lines changed

2 files changed

+51
-5
lines changed

src/ng/compile.js

+7-5
Original file line numberDiff line numberDiff line change
@@ -2480,7 +2480,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
24802480

24812481
// We have transclusion slots,
24822482
// collect them up, compile them and store their transclusion functions
2483-
$template = [];
2483+
$template = window.document.createDocumentFragment();
24842484

24852485
var slotMap = createMap();
24862486
var filledSlots = createMap();
@@ -2508,10 +2508,10 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
25082508
var slotName = slotMap[directiveNormalize(nodeName_(node))];
25092509
if (slotName) {
25102510
filledSlots[slotName] = true;
2511-
slots[slotName] = slots[slotName] || [];
2512-
slots[slotName].push(node);
2511+
slots[slotName] = slots[slotName] || window.document.createDocumentFragment();
2512+
slots[slotName].appendChild(node);
25132513
} else {
2514-
$template.push(node);
2514+
$template.appendChild(node);
25152515
}
25162516
});
25172517

@@ -2525,9 +2525,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
25252525
for (var slotName in slots) {
25262526
if (slots[slotName]) {
25272527
// Only define a transclusion function if the slot was filled
2528-
slots[slotName] = compilationGenerator(mightHaveMultipleTransclusionError, slots[slotName], transcludeFn);
2528+
slots[slotName] = compilationGenerator(mightHaveMultipleTransclusionError, slots[slotName].childNodes, transcludeFn);
25292529
}
25302530
}
2531+
2532+
$template = $template.childNodes;
25312533
}
25322534

25332535
$compileNode.empty(); // clear contents

test/ng/compileSpec.js

+44
Original file line numberDiff line numberDiff line change
@@ -8349,6 +8349,50 @@ describe('$compile', function() {
83498349
});
83508350
});
83518351

8352+
8353+
it('should correctly handle multi-element directives', function() {
8354+
module(function() {
8355+
directive('foo', valueFn({
8356+
template: '[<div ng-transclude></div>]',
8357+
transclude: true
8358+
}));
8359+
directive('bar', valueFn({
8360+
template: '[<div ng-transclude="header"></div>|<div ng-transclude="footer"></div>]',
8361+
transclude: {
8362+
header: 'header',
8363+
footer: 'footer'
8364+
}
8365+
}));
8366+
});
8367+
8368+
inject(function($compile, $rootScope) {
8369+
var tmplWithFoo =
8370+
'<foo>' +
8371+
'<div ng-if-start="true">Hello, </div>' +
8372+
'<div ng-if-end>world!</div>' +
8373+
'</foo>';
8374+
var tmplWithBar =
8375+
'<bar>' +
8376+
'<header ng-if-start="true">This is a </header>' +
8377+
'<header ng-if-end>header!</header>' +
8378+
'<footer ng-if-start="true">This is a </footer>' +
8379+
'<footer ng-if-end>footer!</footer>' +
8380+
'</bar>';
8381+
8382+
var elem1 = $compile(tmplWithFoo)($rootScope);
8383+
var elem2 = $compile(tmplWithBar)($rootScope);
8384+
8385+
$rootScope.$digest();
8386+
8387+
expect(elem1.text()).toBe('[Hello, world!]');
8388+
expect(elem2.text()).toBe('[This is a header!|This is a footer!]');
8389+
8390+
dealoc(elem1);
8391+
dealoc(elem2);
8392+
});
8393+
});
8394+
8395+
83528396
//see issue https://github.com/angular/angular.js/issues/12936
83538397
it('should use the proper scope when it is on the root element of a replaced directive template', function() {
83548398
module(function() {

0 commit comments

Comments
 (0)