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

Commit 159a68e

Browse files
dchermanpetebacondarwin
authored andcommitted
fix(ngTransclude): only compile fallback content if necessary
If the instance of the directive does provide transcluded content, then the fallback content should not be compiled and linked as it will never be used. If the instance of the directive does not provide transcluded content, then the transcluded scope that was created for this non-existent content is never used, so it should be destroy in order to clean up unwanted memory use and digests. Fixes #14768 Fixes #14765 Closes #14775
1 parent b9a56d5 commit 159a68e

File tree

2 files changed

+98
-30
lines changed

2 files changed

+98
-30
lines changed

src/ng/directive/ngTransclude.js

+37-27
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

+61-3
Original file line numberDiff line numberDiff line change
@@ -7889,7 +7889,7 @@ describe('$compile', function() {
78897889
directive('trans', function() {
78907890
return {
78917891
transclude: true,
7892-
template: '<div ng-transclude>old stuff! </div>'
7892+
template: '<div ng-transclude>old stuff!</div>'
78937893
};
78947894
});
78957895
});
@@ -7906,18 +7906,76 @@ describe('$compile', function() {
79067906
directive('trans', function() {
79077907
return {
79087908
transclude: true,
7909-
template: '<div ng-transclude>old stuff! </div>'
7909+
template: '<div ng-transclude>old stuff!</div>'
79107910
};
79117911
});
79127912
});
79137913
inject(function(log, $rootScope, $compile) {
79147914
element = $compile('<div trans></div>')($rootScope);
79157915
$rootScope.$apply();
7916-
expect(sortedHtml(element.html())).toEqual('<div ng-transclude="">old stuff! </div>');
7916+
expect(sortedHtml(element.html())).toEqual('<div ng-transclude=""><span>old stuff!</span></div>');
79177917
});
79187918
});
79197919

79207920

7921+
it('should not compile the fallback content if transcluded content is provided', function() {
7922+
var contentsDidLink = false;
7923+
7924+
module(function() {
7925+
directive('inner', function() {
7926+
return {
7927+
restrict: 'E',
7928+
template: 'old stuff! ',
7929+
link: function() {
7930+
contentsDidLink = true;
7931+
}
7932+
};
7933+
});
7934+
7935+
directive('trans', function() {
7936+
return {
7937+
transclude: true,
7938+
template: '<div ng-transclude><inner></inner></div>'
7939+
};
7940+
});
7941+
});
7942+
inject(function($rootScope, $compile) {
7943+
element = $compile('<div trans>unicorn!</div>')($rootScope);
7944+
$rootScope.$apply();
7945+
expect(sortedHtml(element.html())).toEqual('<div ng-transclude=""><span>unicorn!</span></div>');
7946+
expect(contentsDidLink).toBe(false);
7947+
});
7948+
});
7949+
7950+
it('should compile and link the fallback content if no transcluded content is provided', function() {
7951+
var contentsDidLink = false;
7952+
7953+
module(function() {
7954+
directive('inner', function() {
7955+
return {
7956+
restrict: 'E',
7957+
template: 'old stuff! ',
7958+
link: function() {
7959+
contentsDidLink = true;
7960+
}
7961+
};
7962+
});
7963+
7964+
directive('trans', function() {
7965+
return {
7966+
transclude: true,
7967+
template: '<div ng-transclude><inner></inner></div>'
7968+
};
7969+
});
7970+
});
7971+
inject(function(log, $rootScope, $compile) {
7972+
element = $compile('<div trans></div>')($rootScope);
7973+
$rootScope.$apply();
7974+
expect(sortedHtml(element.html())).toEqual('<div ng-transclude=""><inner>old stuff! </inner></div>');
7975+
expect(contentsDidLink).toBe(true);
7976+
});
7977+
});
7978+
79217979
it('should throw on an ng-transclude element inside no transclusion directive', function() {
79227980
inject(function($rootScope, $compile) {
79237981
// we need to do this because different browsers print empty attributes differently

0 commit comments

Comments
 (0)