-
Notifications
You must be signed in to change notification settings - Fork 27.4k
fix(ngTransclude): correctly support fallback content #14775
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -159,35 +159,45 @@ | |
* </example> | ||
*/ | ||
var ngTranscludeMinErr = minErr('ngTransclude'); | ||
var ngTranscludeDirective = ngDirective({ | ||
restrict: 'EAC', | ||
link: function($scope, $element, $attrs, controller, $transclude) { | ||
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 = ''; | ||
} | ||
|
||
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 = ''; | ||
} | ||
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 was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should probably check to see if any contents exist prior to calling There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not semantically necessary as the compile bails out if there is nothing to compile. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. although it could improve performance slightly |
||
|
||
function ngTranscludeCloneAttachFn(clone) { | ||
if (clone.length) { | ||
$element.empty(); | ||
$element.append(clone); | ||
// 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(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So, this is essentially fixing a leak, right ? Cool |
||
} | ||
} | ||
} | ||
|
||
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 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 (!$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 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); | ||
} | ||
}; | ||
}]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does the fact that we will compile the fallback content later (if necessary) introduce a timing difference with the previous implementation? I.e. is the
$transclude
function called synchronously or will the uncompiled content stay visible for longer?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, so we could add a compile function that extracts the contents and removes if from the document so that it is hidden straight away
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since linking is a sync process (at least as far as ngTransclude is concerned), I'm pretty sure you shouldn't be able to observe the uncompiled content.
The timing of the pre and post linking functions may have changed in relation to to cousin or aunt/uncle directives (yay DOM family!), however I believe that the timing should remain unchanged in relation to ancestor directives which is the more important part.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm mainly concerned about the timing of rendering (so users won't see flashing
{{ ... }}
s).From what I understand from @dcherman's comment (because I was too lazy to read the source code 😛), this hasn't changed. So we're good 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem in this PR with failing to link fallback content if there was a no content provided for an optional named slot. My #14787 fixes that