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 #15554
Closes #15555
This commit is contained in:
George Kalpakas
2018-05-26 17:11:09 +03:00
committed by Martin Staffa
parent b9f19ad0d4
commit 78b9f61366
2 changed files with 51 additions and 5 deletions
+7 -5
View File
@@ -2575,7 +2575,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
// We have transclusion slots,
// collect them up, compile them and store their transclusion functions
$template = [];
$template = window.document.createDocumentFragment();
var slotMap = createMap();
var filledSlots = createMap();
@@ -2603,10 +2603,10 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
var slotName = slotMap[directiveNormalize(nodeName_(node))];
if (slotName) {
filledSlots[slotName] = true;
slots[slotName] = slots[slotName] || [];
slots[slotName].push(node);
slots[slotName] = slots[slotName] || window.document.createDocumentFragment();
slots[slotName].appendChild(node);
} else {
$template.push(node);
$template.appendChild(node);
}
});
@@ -2620,9 +2620,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
for (var slotName in slots) {
if (slots[slotName]) {
// Only define a transclusion function if the slot was filled
slots[slotName] = compilationGenerator(mightHaveMultipleTransclusionError, slots[slotName], transcludeFn);
slots[slotName] = compilationGenerator(mightHaveMultipleTransclusionError, slots[slotName].childNodes, transcludeFn);
}
}
$template = $template.childNodes;
}
$compileNode.empty(); // clear contents
+44
View File
@@ -8843,6 +8843,50 @@ describe('$compile', function() {
});
});
it('should correctly handle multi-element directives', function() {
module(function() {
directive('foo', valueFn({
template: '[<div ng-transclude></div>]',
transclude: true
}));
directive('bar', valueFn({
template: '[<div ng-transclude="header"></div>|<div ng-transclude="footer"></div>]',
transclude: {
header: 'header',
footer: 'footer'
}
}));
});
inject(function($compile, $rootScope) {
var tmplWithFoo =
'<foo>' +
'<div ng-if-start="true">Hello, </div>' +
'<div ng-if-end>world!</div>' +
'</foo>';
var tmplWithBar =
'<bar>' +
'<header ng-if-start="true">This is a </header>' +
'<header ng-if-end>header!</header>' +
'<footer ng-if-start="true">This is a </footer>' +
'<footer ng-if-end>footer!</footer>' +
'</bar>';
var elem1 = $compile(tmplWithFoo)($rootScope);
var elem2 = $compile(tmplWithBar)($rootScope);
$rootScope.$digest();
expect(elem1.text()).toBe('[Hello, world!]');
expect(elem2.text()).toBe('[This is a header!|This is a footer!]');
dealoc(elem1);
dealoc(elem2);
});
});
//see issue https://github.com/angular/angular.js/issues/12936
it('should use the proper scope when it is on the root element of a replaced directive template', function() {
module(function() {