Skip to content

Commit 7c98bb2

Browse files
fix($compile): pass transcludeFn down to nested transclude directives
If you have two directives that both expect to receive transcluded content the outer directive works but the inner directive never receives a transclusion function. This only failed if the first transclude directive was not the first directive found in compilation. Handles the regression identified in e994259 Fixes angular#7240 Closes angular#7387
1 parent b696822 commit 7c98bb2

File tree

2 files changed

+123
-8
lines changed

2 files changed

+123
-8
lines changed

src/ng/compile.js

+15-8
Original file line numberDiff line numberDiff line change
@@ -937,7 +937,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
937937
return linkFnFound ? compositeLinkFn : null;
938938

939939
function compositeLinkFn(scope, nodeList, $rootElement, boundTranscludeFn) {
940-
var nodeLinkFn, childLinkFn, node, $node, childScope, childTranscludeFn, i, ii, n;
940+
var nodeLinkFn, childLinkFn, node, $node, childScope, i, ii, n, childBoundTranscludeFn;
941941

942942
// copy nodeList so that linking doesn't break due to live list updates.
943943
var nodeListLength = nodeList.length,
@@ -959,14 +959,19 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
959959
} else {
960960
childScope = scope;
961961
}
962-
childTranscludeFn = nodeLinkFn.transclude;
963-
if (childTranscludeFn || (!boundTranscludeFn && transcludeFn)) {
964-
nodeLinkFn(childLinkFn, childScope, node, $rootElement,
965-
createBoundTranscludeFn(scope, childTranscludeFn || transcludeFn)
966-
);
962+
963+
// We need to create a new boundTranscludeFn if
964+
// - a directive on this element wants to transclude
965+
// or
966+
// - there is no boundTranscludeFn already and a transcludeFn was passed in
967+
if ( nodeLinkFn.transcludeOnThisElement || (!boundTranscludeFn && transcludeFn) ) {
968+
childBoundTranscludeFn = createBoundTranscludeFn(scope, nodeLinkFn.transclude || transcludeFn);
967969
} else {
968-
nodeLinkFn(childLinkFn, childScope, node, $rootElement, boundTranscludeFn);
970+
childBoundTranscludeFn = boundTranscludeFn;
969971
}
972+
973+
nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn);
974+
970975
} else if (childLinkFn) {
971976
childLinkFn(scope, node.childNodes, undefined, boundTranscludeFn);
972977
}
@@ -1342,7 +1347,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
13421347
}
13431348

13441349
nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true;
1345-
nodeLinkFn.transclude = hasTranscludeDirective && childTranscludeFn;
1350+
nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective;
1351+
nodeLinkFn.transclude = childTranscludeFn;
1352+
13461353
previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective;
13471354

13481355
// might be normal or delayed nodeLinkFn depending on if templateUrl is present

test/ng/compileSpec.js

+108
Original file line numberDiff line numberDiff line change
@@ -1497,6 +1497,114 @@ describe('$compile', function() {
14971497
));
14981498

14991499

1500+
describe('nested transcludes', function() {
1501+
1502+
beforeEach(module(function($compileProvider) {
1503+
1504+
$compileProvider.directive('noop', valueFn({}));
1505+
1506+
$compileProvider.directive('sync', valueFn({
1507+
template: '<div ng-transclude></div>',
1508+
transclude: true
1509+
}));
1510+
1511+
$compileProvider.directive('async', valueFn({
1512+
templateUrl: 'async',
1513+
transclude: true
1514+
}));
1515+
1516+
$compileProvider.directive('syncSync', valueFn({
1517+
template: '<div noop><div sync><div ng-transclude></div></div></div>',
1518+
transclude: true
1519+
}));
1520+
1521+
$compileProvider.directive('syncAsync', valueFn({
1522+
template: '<div noop><div async><div ng-transclude></div></div></div>',
1523+
transclude: true
1524+
}));
1525+
1526+
$compileProvider.directive('asyncSync', valueFn({
1527+
templateUrl: 'asyncSync',
1528+
transclude: true
1529+
}));
1530+
1531+
$compileProvider.directive('asyncAsync', valueFn({
1532+
templateUrl: 'asyncAsync',
1533+
transclude: true
1534+
}));
1535+
1536+
}));
1537+
1538+
beforeEach(inject(function($templateCache) {
1539+
$templateCache.put('async', '<div ng-transclude></div>');
1540+
$templateCache.put('asyncSync', '<div noop><div sync><div ng-transclude></div></div></div>');
1541+
$templateCache.put('asyncAsync', '<div noop><div async><div ng-transclude></div></div></div>');
1542+
}));
1543+
1544+
1545+
it('should allow nested transclude directives with sync template containing sync template', inject(function($compile, $rootScope) {
1546+
element = $compile('<div sync-sync>transcluded content</div>')($rootScope);
1547+
$rootScope.$digest();
1548+
expect(element.text()).toEqual('transcluded content');
1549+
}));
1550+
1551+
it('should allow nested transclude directives with sync template containing async template', inject(function($compile, $rootScope) {
1552+
element = $compile('<div sync-async>transcluded content</div>')($rootScope);
1553+
$rootScope.$digest();
1554+
expect(element.text()).toEqual('transcluded content');
1555+
}));
1556+
1557+
it('should allow nested transclude directives with async template containing sync template', inject(function($compile, $rootScope) {
1558+
element = $compile('<div async-sync>transcluded content</div>')($rootScope);
1559+
$rootScope.$digest();
1560+
expect(element.text()).toEqual('transcluded content');
1561+
}));
1562+
1563+
it('should allow nested transclude directives with async template containing asynch template', inject(function($compile, $rootScope) {
1564+
element = $compile('<div async-async>transcluded content</div>')($rootScope);
1565+
$rootScope.$digest();
1566+
expect(element.text()).toEqual('transcluded content');
1567+
}));
1568+
});
1569+
1570+
describe('transclude and children', function() {
1571+
beforeEach(module(function($compileProvider) {
1572+
1573+
$compileProvider.directive('myExample', valueFn({
1574+
scope: {},
1575+
link: function link(scope, element, attrs) {
1576+
var foo = element[0].querySelector('.foo');
1577+
scope.children = angular.element(foo).children().length;
1578+
},
1579+
template: '<div>' +
1580+
'<div>myExample {{children}}!</div>' +
1581+
'<div ng-if="children">has children</div>' +
1582+
'<div class="foo" ng-transclude></div>' +
1583+
'</div>',
1584+
transclude: true
1585+
1586+
}));
1587+
1588+
}));
1589+
1590+
it("should not pick up too many children when transcluding", inject(function($compile, $rootScope) {
1591+
var element = $compile('<div my-example></div>')($rootScope);
1592+
$rootScope.$digest();
1593+
$rootScope.$digest();
1594+
expect(element.text()).toEqual('myExample 0!');
1595+
dealoc(element);
1596+
1597+
element = $compile('<div my-example><p></p></div>')($rootScope);
1598+
$rootScope.$digest();
1599+
$rootScope.$digest();
1600+
expect(element.text()).toEqual('myExample 1!has children');
1601+
dealoc(element);
1602+
}));
1603+
1604+
});
1605+
1606+
1607+
15001608
it("should fail if replacing and template doesn't have a single root element", function() {
15011609
module(function($exceptionHandlerProvider) {
15021610
$exceptionHandlerProvider.mode('log');

0 commit comments

Comments
 (0)