From 024b5fc2b242c4de664618d54a1e9f6e5518d85e Mon Sep 17 00:00:00 2001 From: Peter Bacon Darwin Date: Thu, 15 Sep 2016 11:59:42 +0100 Subject: [PATCH] fix(ngTransclude): use fallback content if only whitespace is provided If the transcluded content is only whitespace then we should use the fallback content instead. This allows more flexibility in formatting your HTML. Closes #15077 BREAKING CHANGE: Previously whitespace only transclusion would be treated as the transclusion being "not empty", which meant that fallback content was not used in that case. Now if you only provide whitespace as the transclusion content, it will be assumed to be empty and the fallback content will be used instead. If you really do want whitespace then you can force it to be used by adding a comment to the whitespace. --- src/ng/directive/ngTransclude.js | 15 +++++++-- test/ng/compileSpec.js | 54 ++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 3 deletions(-) diff --git a/src/ng/directive/ngTransclude.js b/src/ng/directive/ngTransclude.js index f19c251fc3b6..4f48f5ee0bbf 100644 --- a/src/ng/directive/ngTransclude.js +++ b/src/ng/directive/ngTransclude.js @@ -13,8 +13,8 @@ * * If the transcluded content is not empty (i.e. contains one or more DOM nodes, including whitespace text nodes), any existing * content of this element will be removed before the transcluded content is inserted. - * If the transcluded content is empty, the existing content is left intact. This lets you provide fallback content in the case - * that no transcluded content is provided. + * If the transcluded content is empty (or only whitespace), the existing content is left intact. This lets you provide fallback + * content in the case that no transcluded content is provided. * * @element ANY * @@ -195,7 +195,7 @@ var ngTranscludeDirective = ['$compile', function($compile) { } function ngTranscludeCloneAttachFn(clone, transcludedScope) { - if (clone.length) { + if (clone.length && notWhitespace(clone)) { $element.append(clone); } else { useFallbackContent(); @@ -212,6 +212,15 @@ var ngTranscludeDirective = ['$compile', function($compile) { $element.append(clone); }); } + + function notWhitespace(nodes) { + for (var i = 0, ii = nodes.length; i < ii; i++) { + var node = nodes[i]; + if (node.nodeType !== NODE_TYPE_TEXT || node.nodeValue.trim()) { + return true; + } + } + } }; } }; diff --git a/test/ng/compileSpec.js b/test/ng/compileSpec.js index 70112d14a82f..a9659524fd21 100755 --- a/test/ng/compileSpec.js +++ b/test/ng/compileSpec.js @@ -8728,6 +8728,60 @@ describe('$compile', function() { }); }); + it('should compile and link the fallback content if only whitespace transcluded content is provided', function() { + var linkSpy = jasmine.createSpy('postlink'); + + module(function() { + directive('inner', function() { + return { + restrict: 'E', + template: 'old stuff! ', + link: linkSpy + }; + }); + + directive('trans', function() { + return { + transclude: true, + template: '
' + }; + }); + }); + inject(function(log, $rootScope, $compile) { + element = $compile('
\n \n
')($rootScope); + $rootScope.$apply(); + expect(sortedHtml(element.html())).toEqual('
old stuff!
'); + expect(linkSpy).toHaveBeenCalled(); + }); + }); + + it('should not link the fallback content if only whitespace and comments are provided as transclude content', function() { + var linkSpy = jasmine.createSpy('postlink'); + + module(function() { + directive('inner', function() { + return { + restrict: 'E', + template: 'old stuff! ', + link: linkSpy + }; + }); + + directive('trans', function() { + return { + transclude: true, + template: '
' + }; + }); + }); + inject(function(log, $rootScope, $compile) { + element = $compile('
\n \n
')($rootScope); + $rootScope.$apply(); + expect(sortedHtml(element.html())).toEqual('
\n \n
'); + expect(linkSpy).not.toHaveBeenCalled(); + }); + }); + it('should compile and link the fallback content if an optional transclusion slot is not provided', function() { var linkSpy = jasmine.createSpy('postlink');