Skip to content

Commit

Permalink
fix($compile): pass transcludeFn down to nested transclude directives
Browse files Browse the repository at this point in the history
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
  • Loading branch information
petebacondarwin authored and vojtajina committed May 28, 2014
1 parent a4784eb commit 30ed428
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 8 deletions.
23 changes: 15 additions & 8 deletions src/ng/compile.js
Original file line number Diff line number Diff line change
Expand Up @@ -936,7 +936,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, childBoundTranscludeFn;

// copy nodeList so that linking doesn't break due to live list updates.
var nodeListLength = nodeList.length,
Expand All @@ -958,14 +958,19 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
} else {
childScope = scope;
}
childTranscludeFn = nodeLinkFn.transclude;
if (childTranscludeFn || (!boundTranscludeFn && transcludeFn)) {
nodeLinkFn(childLinkFn, childScope, node, $rootElement,
createBoundTranscludeFn(scope, childTranscludeFn || transcludeFn)
);

// 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) ) {
childBoundTranscludeFn = createBoundTranscludeFn(scope, nodeLinkFn.transclude || transcludeFn);
} else {
nodeLinkFn(childLinkFn, childScope, node, $rootElement, boundTranscludeFn);
childBoundTranscludeFn = boundTranscludeFn;
}

nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn);

} else if (childLinkFn) {
childLinkFn(scope, node.childNodes, undefined, boundTranscludeFn);
}
Expand Down Expand Up @@ -1341,7 +1346,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
Expand Down
109 changes: 109 additions & 0 deletions test/ng/compileSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4206,6 +4206,115 @@ describe('$compile', function() {
});

});


describe('nested transcludes', function() {

beforeEach(module(function($compileProvider) {

$compileProvider.directive('noop', valueFn({}));

$compileProvider.directive('sync', valueFn({
template: '<div ng-transclude></div>',
transclude: true
}));

$compileProvider.directive('async', valueFn({
templateUrl: 'async',
transclude: true
}));

$compileProvider.directive('syncSync', valueFn({
template: '<div noop><div sync><div ng-transclude></div></div></div>',
transclude: true
}));

$compileProvider.directive('syncAsync', valueFn({
template: '<div noop><div async><div ng-transclude></div></div></div>',
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', '<div ng-transclude></div>');
$templateCache.put('asyncSync', '<div noop><div sync><div ng-transclude></div></div></div>');
$templateCache.put('asyncAsync', '<div noop><div async><div ng-transclude></div></div></div>');
}));


it('should allow nested transclude directives with sync template containing sync template', inject(function($compile, $rootScope) {
element = $compile('<div sync-sync>transcluded content</div>')($rootScope);
$rootScope.$digest();
expect(element.text()).toEqual('transcluded content');
}));

it('should allow nested transclude directives with sync template containing async template', inject(function($compile, $rootScope) {
element = $compile('<div sync-async>transcluded content</div>')($rootScope);
$rootScope.$digest();
expect(element.text()).toEqual('transcluded content');
}));

it('should allow nested transclude directives with async template containing sync template', inject(function($compile, $rootScope) {
element = $compile('<div async-sync>transcluded content</div>')($rootScope);
$rootScope.$digest();
expect(element.text()).toEqual('transcluded content');
}));

it('should allow nested transclude directives with async template containing asynch template', inject(function($compile, $rootScope) {
element = $compile('<div async-async>transcluded content</div>')($rootScope);
$rootScope.$digest();
expect(element.text()).toEqual('transcluded content');
}));
});


describe('multiple siblings receiving transclusion', function() {

it("should only receive transclude from parent", function() {

module(function($compileProvider) {

$compileProvider.directive('myExample', valueFn({
scope: {},
link: function link(scope, element, attrs) {
var foo = element[0].querySelector('.foo');
scope.children = angular.element(foo).children().length;
},
template: '<div>' +
'<div>myExample {{children}}!</div>' +
'<div ng-if="children">has children</div>' +
'<div class="foo" ng-transclude></div>' +
'</div>',
transclude: true

}));

});

inject(function($compile, $rootScope) {
var element = $compile('<div my-example></div>')($rootScope);
$rootScope.$digest();
expect(element.text()).toEqual('myExample 0!');
dealoc(element);

element = $compile('<div my-example><p></p></div>')($rootScope);
$rootScope.$digest();
expect(element.text()).toEqual('myExample 1!has children');
dealoc(element);
});
});
});
});


Expand Down

0 comments on commit 30ed428

Please sign in to comment.