diff --git a/src/ng/directive/ngTransclude.js b/src/ng/directive/ngTransclude.js index 5b31b06a2248..6b02963f16d9 100644 --- a/src/ng/directive/ngTransclude.js +++ b/src/ng/directive/ngTransclude.js @@ -163,41 +163,56 @@ var ngTranscludeDirective = ['$compile', function($compile) { return { restrict: 'EAC', terminal: true, - link: function($scope, $element, $attrs, controller, $transclude) { - if ($attrs.ngTransclude === $attrs.$attr.ngTransclude) { - // If the attribute is of the form: `ng-transclude="ng-transclude"` - // then treat it like the default - $attrs.ngTransclude = ''; - } + compile: function ngTranscludeCompile(tElement) { + + // Remove and cache any original content to act as a fallback + var fallbackLinkFn = $compile(tElement.contents()); + tElement.empty(); + + return function ngTranscludePostLink($scope, $element, $attrs, controller, $transclude) { + + if (!$transclude) { + throw ngTranscludeMinErr('orphan', + 'Illegal use of ngTransclude directive in the template! ' + + 'No parent directive that requires a transclusion found. ' + + 'Element: {0}', + startingTag($element)); + } - function ngTranscludeCloneAttachFn(clone, transcludedScope) { - if (clone.length) { - $element.empty(); - $element.append(clone); - } else { - // Since this is the fallback content rather than the transcluded content, - // we compile against the scope we were linked against rather than the transcluded - // scope since this is the directive's own content - $compile($element.contents())($scope); - // There is nothing linked against the transcluded scope since no content was available, - // so it should be safe to clean up the generated scope. - transcludedScope.$destroy(); + // If the attribute is of the form: `ng-transclude="ng-transclude"` then treat it like the default + if ($attrs.ngTransclude === $attrs.$attr.ngTransclude) { + $attrs.ngTransclude = ''; } - } + var slotName = $attrs.ngTransclude || $attrs.ngTranscludeSlot; - if (!$transclude) { - throw ngTranscludeMinErr('orphan', - 'Illegal use of ngTransclude directive in the template! ' + - 'No parent directive that requires a transclusion found. ' + - 'Element: {0}', - startingTag($element)); - } + // If the slot is required and no transclusion content is provided then this call will throw an error + $transclude(ngTranscludeCloneAttachFn, null, slotName); - // If there is no slot name defined or the slot name is not optional - // then transclude the slot - var slotName = $attrs.ngTransclude || $attrs.ngTranscludeSlot; - $transclude(ngTranscludeCloneAttachFn, null, slotName); + // If the slot is optional and no transclusion content is provided then use the fallback content + if (slotName && !$transclude.isSlotFilled(slotName)) { + useFallbackContent(); + } + + function ngTranscludeCloneAttachFn(clone, transcludedScope) { + if (clone.length) { + $element.append(clone); + } else { + useFallbackContent(); + // There is nothing linked against the transcluded scope since no content was available, + // so it should be safe to clean up the generated scope. + transcludedScope.$destroy(); + } + } + + function useFallbackContent() { + // Since this is the fallback content rather than the transcluded content, + // we link against the scope of this directive rather than the transcluded scope + fallbackLinkFn($scope, function(clone) { + $element.append(clone); + }); + } + }; } }; }]; diff --git a/test/ng/compileSpec.js b/test/ng/compileSpec.js index d1ef1d3b2f3a..e8d9d277ea99 100755 --- a/test/ng/compileSpec.js +++ b/test/ng/compileSpec.js @@ -7970,17 +7970,52 @@ describe('$compile', function() { }); - it('should not compile the fallback content if transcluded content is provided', function() { - var contentsDidLink = false; + it('should clear the fallback content from the element during compile and before linking', function() { + module(function() { + directive('trans', function() { + return { + transclude: true, + template: '