I have a directive (a progressbar) which should have two possible states, one without any description and one with a label on the left side. It would be cool to simple use
Based on the solution from @plong0 & @Ilan, this seems to work a bit better since it works with whitespace as well.
$transcludeFn(function(clonedElement) {
scope.hasTranscludedContent = clonedElement.html().trim() === "";
});
where previously <my-directive> </my-directive>
would return that it has a .length
of 1
since it contains a text node. since the function passed into $transcludeFn
returns a jQuery object of the contents of the transcluded content, we can just get the inner text, remove whitespace on the ends, and check to see if it's blank or not.
Note that this only checks for text, so including html elements without text will also be flagged as empty. Like this: <my-directive> <span> </span> </my-directive>
- This worked for my needs though.
Based on @Ilan's solution, you can use this simple $transclude function to know if there is transcluded content or not.
$transclude(function(clone){
if(clone.length){
scope.hasTranscluded = true;
}
});
Plnkr demonstrating this approach with ng-if to set default content if nothing to transclude: http://plnkr.co/hHr0aoSktqZYKoiFMzE6
Here is a plunker: http://plnkr.co/edit/ednJwiceWD5vS0orewKW?p=preview
You can find the transcluded element inside the linking function and check it's contents:
Directive:
app.directive('progressbar', function(){
return {
scope: {},
transclude: true,
templateUrl: "progressbar.html",
link: function(scope,elm){
var transcluded = elm.find('span').contents();
scope.withLabel = transcluded.length > 0; // true or false
}
}
})
Template:
<div class="progress" ng-class="{'with-label': withLabel}">
<div class="label"><span ng-transclude></span>
<div class="other">...</div>
</div>
You can also create your custom transclusion directive like so:
app.directive('myTransclude', function(){
return {
link: function(scope, elm, attrs, ctrl, $transclude){
$transclude(function(clone){
// Do something with this:
// if(clone.length > 0) ...
elm.empty();
elm.append(clone);
})
}
}
})
After release of Angular v1.5 with multi-slot transclusion it's even simpler. For example you have used component
instead of directive
and don't have access to link
or compile
functions. Yet you have access to $transclude
service. So you can check presence of content with 'official' method:
app.component('myTransclude', {
transclude: {
'slot': '?transcludeSlot'
},
controller: function ($transclude) {
this.transcludePresent = function() {
return $transclude.isSlotFilled('slot');
};
}
})
with template like this:
<div class="progress" ng-class="{'with-label': withLabel}">
<div class="label"><span ng-transclude="slot"></span>
<div class="other">...</div>
</div>