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

Commit 9b4db0c

Browse files
committed
fix(ngTransclude): correct support fallback content
If the transclude function did not return content, then the transcluded scope that was created needs to be cleaned up in order to avoid a slight amount of unnecessary overhead since the additional scope is no longer needed. If the transcluded content did return content, the the fallback content should never have been linked in the first place as it was intended to be immediately removed. Fixes #14768 Fixes #14765
1 parent 8cd9848 commit 9b4db0c

File tree

2 files changed

+66
-30
lines changed

2 files changed

+66
-30
lines changed

src/ng/directive/ngTransclude.js

Lines changed: 37 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -159,35 +159,45 @@
159159
* </example>
160160
*/
161161
var ngTranscludeMinErr = minErr('ngTransclude');
162-
var ngTranscludeDirective = ngDirective({
163-
restrict: 'EAC',
164-
link: function($scope, $element, $attrs, controller, $transclude) {
162+
var ngTranscludeDirective = ['$compile', function($compile) {
163+
return {
164+
restrict: 'EAC',
165+
terminal: true,
166+
link: function($scope, $element, $attrs, controller, $transclude) {
167+
if ($attrs.ngTransclude === $attrs.$attr.ngTransclude) {
168+
// If the attribute is of the form: `ng-transclude="ng-transclude"`
169+
// then treat it like the default
170+
$attrs.ngTransclude = '';
171+
}
165172

166-
if ($attrs.ngTransclude === $attrs.$attr.ngTransclude) {
167-
// If the attribute is of the form: `ng-transclude="ng-transclude"`
168-
// then treat it like the default
169-
$attrs.ngTransclude = '';
170-
}
173+
function ngTranscludeCloneAttachFn(clone, transcludedScope) {
174+
if (clone.length) {
175+
$element.empty();
176+
$element.append(clone);
177+
} else {
178+
// Since this is the fallback content rather than the transcluded content,
179+
// we compile against the scope we were linked against rather than the transcluded
180+
// scope since this is the directive's own content
181+
$compile($element.contents())($scope);
171182

172-
function ngTranscludeCloneAttachFn(clone) {
173-
if (clone.length) {
174-
$element.empty();
175-
$element.append(clone);
183+
// There is nothing linked against the transcluded scope since no content was available,
184+
// so it should be safe to clean up the generated scope.
185+
transcludedScope.$destroy();
186+
}
176187
}
177-
}
178188

179-
if (!$transclude) {
180-
throw ngTranscludeMinErr('orphan',
181-
'Illegal use of ngTransclude directive in the template! ' +
182-
'No parent directive that requires a transclusion found. ' +
183-
'Element: {0}',
184-
startingTag($element));
185-
}
186-
187-
// If there is no slot name defined or the slot name is not optional
188-
// then transclude the slot
189-
var slotName = $attrs.ngTransclude || $attrs.ngTranscludeSlot;
190-
$transclude(ngTranscludeCloneAttachFn, null, slotName);
191-
}
192-
});
189+
if (!$transclude) {
190+
throw ngTranscludeMinErr('orphan',
191+
'Illegal use of ngTransclude directive in the template! ' +
192+
'No parent directive that requires a transclusion found. ' +
193+
'Element: {0}',
194+
startingTag($element));
195+
}
193196

197+
// If there is no slot name defined or the slot name is not optional
198+
// then transclude the slot
199+
var slotName = $attrs.ngTransclude || $attrs.ngTranscludeSlot;
200+
$transclude(ngTranscludeCloneAttachFn, null, slotName);
201+
}
202+
};
203+
}];

test/ng/compileSpec.js

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7937,35 +7937,61 @@ describe('$compile', function() {
79377937

79387938
it('should clear contents of the ng-translude element before appending transcluded content' +
79397939
' if transcluded content exists', function() {
7940+
var contentsDidLink = false;
7941+
79407942
module(function() {
7943+
directive('inner', function() {
7944+
return {
7945+
restrict: 'E',
7946+
template: 'old stuff! ',
7947+
link: function() {
7948+
contentsDidLink = true;
7949+
}
7950+
};
7951+
});
7952+
79417953
directive('trans', function() {
79427954
return {
79437955
transclude: true,
7944-
template: '<div ng-transclude>old stuff! </div>'
7956+
template: '<div ng-transclude><inner></inner></div>'
79457957
};
79467958
});
79477959
});
79487960
inject(function($rootScope, $compile) {
79497961
element = $compile('<div trans>unicorn!</div>')($rootScope);
79507962
$rootScope.$apply();
79517963
expect(sortedHtml(element.html())).toEqual('<div ng-transclude="">unicorn!</div>');
7964+
expect(contentsDidLink).toBe(false);
79527965
});
79537966
});
79547967

79557968
it('should NOT clear contents of the ng-translude element before appending transcluded content' +
79567969
' if transcluded content does NOT exist', function() {
7970+
var contentsDidLink = false;
7971+
79577972
module(function() {
7973+
directive('inner', function() {
7974+
return {
7975+
restrict: 'E',
7976+
template: 'old stuff! ',
7977+
link: function() {
7978+
contentsDidLink = true;
7979+
}
7980+
};
7981+
});
7982+
79587983
directive('trans', function() {
79597984
return {
79607985
transclude: true,
7961-
template: '<div ng-transclude>old stuff! </div>'
7986+
template: '<div ng-transclude><inner></inner></div>'
79627987
};
79637988
});
79647989
});
79657990
inject(function(log, $rootScope, $compile) {
79667991
element = $compile('<div trans></div>')($rootScope);
79677992
$rootScope.$apply();
7968-
expect(sortedHtml(element.html())).toEqual('<div ng-transclude="">old stuff! </div>');
7993+
expect(sortedHtml(element.html())).toEqual('<div ng-transclude=""><inner>old stuff! </inner></div>');
7994+
expect(contentsDidLink).toBe(true);
79697995
});
79707996
});
79717997

0 commit comments

Comments
 (0)