From 4f03dc5a9650f3f22f78b438474322b4b8871dec Mon Sep 17 00:00:00 2001 From: Vojta Jina Date: Wed, 14 May 2014 14:25:54 +0200 Subject: [PATCH] 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. Fixes #7240 Closes #7387 --- src/ng/compile.js | 23 ++++++++------ test/ng/compileSpec.js | 72 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 9 deletions(-) diff --git a/src/ng/compile.js b/src/ng/compile.js index cfa72d972e67..5d07dc785342 100644 --- a/src/ng/compile.js +++ b/src/ng/compile.js @@ -941,7 +941,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { return linkFnFound ? compositeLinkFn : null; function compositeLinkFn(scope, nodeList, $rootElement, boundTranscludeFn) { - var nodeLinkFn, childLinkFn, node, $node, childScope, childTranscludeFn, i, ii, n; + var nodeLinkFn, childLinkFn, node, $node, childScope, i, ii, n; // copy nodeList so that linking doesn't break due to live list updates. var nodeListLength = nodeList.length, @@ -963,14 +963,17 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { } else { childScope = scope; } - childTranscludeFn = nodeLinkFn.transclude; - if (childTranscludeFn || (!boundTranscludeFn && transcludeFn)) { - nodeLinkFn(childLinkFn, childScope, node, $rootElement, - createBoundTranscludeFn(scope, childTranscludeFn || transcludeFn) - ); - } else { - nodeLinkFn(childLinkFn, childScope, node, $rootElement, boundTranscludeFn); + + // We need to create a new boundTranscludeFn if + // - a directive on this element wants to transclude + // or + // - there is no boundTranscludeFn already and a transcludeFn was passed in + if ( nodeLinkFn.transcludeOnThisElement || (!boundTranscludeFn && transcludeFn) ) { + boundTranscludeFn = createBoundTranscludeFn(scope, nodeLinkFn.transclude || transcludeFn); } + + nodeLinkFn(childLinkFn, childScope, node, $rootElement, boundTranscludeFn); + } else if (childLinkFn) { childLinkFn(scope, node.childNodes, undefined, boundTranscludeFn); } @@ -1346,7 +1349,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { } nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true; - nodeLinkFn.transclude = hasTranscludeDirective && childTranscludeFn; + nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective; + nodeLinkFn.transclude = childTranscludeFn; + previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective; // might be normal or delayed nodeLinkFn depending on if templateUrl is present diff --git a/test/ng/compileSpec.js b/test/ng/compileSpec.js index b3af196f3bb7..154d56205d8c 100755 --- a/test/ng/compileSpec.js +++ b/test/ng/compileSpec.js @@ -1497,6 +1497,78 @@ describe('$compile', function() { )); + describe('nested transcludes', function() { + + beforeEach(module(function($compileProvider) { + + $compileProvider.directive('noop', valueFn({})); + + $compileProvider.directive('sync', valueFn({ + template: '
', + transclude: true + })); + + $compileProvider.directive('async', valueFn({ + templateUrl: 'async', + transclude: true + })); + + $compileProvider.directive('syncSync', valueFn({ + template: '
', + transclude: true + })); + + $compileProvider.directive('syncAsync', valueFn({ + template: '
', + transclude: true + })); + + $compileProvider.directive('asyncSync', valueFn({ + templateUrl: 'asyncSync', + transclude: true + })); + + $compileProvider.directive('asyncAsync', valueFn({ + templateUrl: 'asyncAsync', + transclude: true + })); + + })); + + beforeEach(inject(function($templateCache) { + $templateCache.put('async', '
'); + $templateCache.put('asyncSync', '
'); + $templateCache.put('asyncAsync', '
'); + })); + + + it('should allow nested transclude directives with sync template containing sync template', inject(function($compile, $rootScope) { + element = $compile('
transcluded content
')($rootScope); + $rootScope.$digest(); + expect(element.text().trim()).toEqual('transcluded content'); + })); + + it('should allow nested transclude directives with sync template containing async template', inject(function($compile, $rootScope) { + element = $compile('
transcluded content
')($rootScope); + $rootScope.$digest(); + expect(element.text().trim()).toEqual('transcluded content'); + })); + + it('should allow nested transclude directives with async template containing sync template', inject(function($compile, $rootScope) { + element = $compile('
transcluded content
')($rootScope); + $rootScope.$digest(); + expect(element.text().trim()).toEqual('transcluded content'); + })); + + it('should allow nested transclude directives with async template containing asynch template', inject(function($compile, $rootScope) { + element = $compile('
transcluded content
')($rootScope); + $rootScope.$digest(); + expect(element.text().trim()).toEqual('transcluded content'); + })); + }); + + + it("should fail if replacing and template doesn't have a single root element", function() { module(function($exceptionHandlerProvider) { $exceptionHandlerProvider.mode('log');