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

Commit d48d129

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 #7240 Closes #7387
1 parent a5ca9f7 commit d48d129

File tree

2 files changed

+124
-8
lines changed

2 files changed

+124
-8
lines changed

src/ng/compile.js

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

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

941941
// copy nodeList so that linking doesn't break due to live list updates.
942942
var nodeListLength = nodeList.length,
@@ -958,14 +958,19 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
958958
} else {
959959
childScope = scope;
960960
}
961-
childTranscludeFn = nodeLinkFn.transclude;
962-
if (childTranscludeFn || (!boundTranscludeFn && transcludeFn)) {
963-
nodeLinkFn(childLinkFn, childScope, node, $rootElement,
964-
createBoundTranscludeFn(scope, childTranscludeFn || transcludeFn)
965-
);
961+
962+
// We need to create a new boundTranscludeFn if
963+
// - a directive on this element wants to transclude
964+
// or
965+
// - there is no boundTranscludeFn already and a transcludeFn was passed in
966+
if ( nodeLinkFn.transcludeOnThisElement || (!boundTranscludeFn && transcludeFn) ) {
967+
childBoundTranscludeFn = createBoundTranscludeFn(scope, nodeLinkFn.transclude || transcludeFn);
966968
} else {
967-
nodeLinkFn(childLinkFn, childScope, node, $rootElement, boundTranscludeFn);
969+
childBoundTranscludeFn = boundTranscludeFn;
968970
}
971+
972+
nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn);
973+
969974
} else if (childLinkFn) {
970975
childLinkFn(scope, node.childNodes, undefined, boundTranscludeFn);
971976
}
@@ -1341,7 +1346,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
13411346
}
13421347

13431348
nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true;
1344-
nodeLinkFn.transclude = hasTranscludeDirective && childTranscludeFn;
1349+
nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective;
1350+
nodeLinkFn.transclude = childTranscludeFn;
1351+
13451352
previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective;
13461353

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

test/ng/compileSpec.js

+109
Original file line numberDiff line numberDiff line change
@@ -4112,6 +4112,115 @@ describe('$compile', function() {
41124112
});
41134113

41144114
});
4115+
4116+
4117+
describe('nested transcludes', function() {
4118+
4119+
beforeEach(module(function($compileProvider) {
4120+
4121+
$compileProvider.directive('noop', valueFn({}));
4122+
4123+
$compileProvider.directive('sync', valueFn({
4124+
template: '<div ng-transclude></div>',
4125+
transclude: true
4126+
}));
4127+
4128+
$compileProvider.directive('async', valueFn({
4129+
templateUrl: 'async',
4130+
transclude: true
4131+
}));
4132+
4133+
$compileProvider.directive('syncSync', valueFn({
4134+
template: '<div noop><div sync><div ng-transclude></div></div></div>',
4135+
transclude: true
4136+
}));
4137+
4138+
$compileProvider.directive('syncAsync', valueFn({
4139+
template: '<div noop><div async><div ng-transclude></div></div></div>',
4140+
transclude: true
4141+
}));
4142+
4143+
$compileProvider.directive('asyncSync', valueFn({
4144+
templateUrl: 'asyncSync',
4145+
transclude: true
4146+
}));
4147+
4148+
$compileProvider.directive('asyncAsync', valueFn({
4149+
templateUrl: 'asyncAsync',
4150+
transclude: true
4151+
}));
4152+
4153+
}));
4154+
4155+
beforeEach(inject(function($templateCache) {
4156+
$templateCache.put('async', '<div ng-transclude></div>');
4157+
$templateCache.put('asyncSync', '<div noop><div sync><div ng-transclude></div></div></div>');
4158+
$templateCache.put('asyncAsync', '<div noop><div async><div ng-transclude></div></div></div>');
4159+
}));
4160+
4161+
4162+
it('should allow nested transclude directives with sync template containing sync template', inject(function($compile, $rootScope) {
4163+
element = $compile('<div sync-sync>transcluded content</div>')($rootScope);
4164+
$rootScope.$digest();
4165+
expect(element.text()).toEqual('transcluded content');
4166+
}));
4167+
4168+
it('should allow nested transclude directives with sync template containing async template', inject(function($compile, $rootScope) {
4169+
element = $compile('<div sync-async>transcluded content</div>')($rootScope);
4170+
$rootScope.$digest();
4171+
expect(element.text()).toEqual('transcluded content');
4172+
}));
4173+
4174+
it('should allow nested transclude directives with async template containing sync template', inject(function($compile, $rootScope) {
4175+
element = $compile('<div async-sync>transcluded content</div>')($rootScope);
4176+
$rootScope.$digest();
4177+
expect(element.text()).toEqual('transcluded content');
4178+
}));
4179+
4180+
it('should allow nested transclude directives with async template containing asynch template', inject(function($compile, $rootScope) {
4181+
element = $compile('<div async-async>transcluded content</div>')($rootScope);
4182+
$rootScope.$digest();
4183+
expect(element.text()).toEqual('transcluded content');
4184+
}));
4185+
});
4186+
4187+
4188+
describe('multiple siblings receiving transclusion', function() {
4189+
4190+
it("should only receive transclude from parent", function() {
4191+
4192+
module(function($compileProvider) {
4193+
4194+
$compileProvider.directive('myExample', valueFn({
4195+
scope: {},
4196+
link: function link(scope, element, attrs) {
4197+
var foo = element[0].querySelector('.foo');
4198+
scope.children = angular.element(foo).children().length;
4199+
},
4200+
template: '<div>' +
4201+
'<div>myExample {{children}}!</div>' +
4202+
'<div ng-if="children">has children</div>' +
4203+
'<div class="foo" ng-transclude></div>' +
4204+
'</div>',
4205+
transclude: true
4206+
4207+
}));
4208+
4209+
});
4210+
4211+
inject(function($compile, $rootScope) {
4212+
var element = $compile('<div my-example></div>')($rootScope);
4213+
$rootScope.$digest();
4214+
expect(element.text()).toEqual('myExample 0!');
4215+
dealoc(element);
4216+
4217+
element = $compile('<div my-example><p></p></div>')($rootScope);
4218+
$rootScope.$digest();
4219+
expect(element.text()).toEqual('myExample 1!has children');
4220+
dealoc(element);
4221+
});
4222+
});
4223+
});
41154224
});
41164225

41174226

0 commit comments

Comments
 (0)