16

Is there any way to transclude some content into a directive without adding extra elements.

For example

directive:

{
    scope: {
        someParam: "="
    },
    link: function(scope, element, attrs){
        //do something
    },
    transclude: true,
    template:'<div ng-transclude></div>'
}

source html:

<div my-directive some-param="somethingFromController">
    my transcluded content: {{somethingElseFromController}}
</div>

With this example an extra div gets added to the markup. Normally this would be fine but I'm trying to use this directive inside a table so adding a div tag screws things up.

I also tried not specifying transclude or template which gets rid of the extra div tag but now {{somethingElseFromController}} cannot be found as the "transcluded" content is in an isolated scope. I know I could just get the parameters for my directive from the attrs object in the linking function instead of creating an isolated scope but I'd rather avoid needing to evaluate strings with scope.$apply().

Anyone know how to accomplish this? Thanks!

4
  • What is the "transcluded" content that somethingElseFromController is meant to provide? Commented Oct 16, 2013 at 18:52
  • Could be anything. e.g. a string from the controller. I just need my transcluded content to be in the same scope as the controller. Commented Oct 16, 2013 at 18:54
  • 1
    angular needs an element to 'hang' the scope on. However, if you are just trying to add behaviour, then as you said, you don't need a template. Yes, otherwise, use the parent scope. Commented Oct 16, 2013 at 19:18
  • Alright thanks for the info. I thought about doing that but then I will loose the tag type form the markup and I'll have to guess at what it should be from the directive template. And I would also loose any other attributes on the markup tag being replaced....oh well I'll probably just bite the bulled and use attrs instead of creating an isolated scope Commented Oct 16, 2013 at 19:23

3 Answers 3

8

What @Vakey answered is what I was searching for.

But as today, the Angular documentation says:

The transclude function that is passed to the compile function is deprecated, as it e.g. does not know about the right outer scope. Please use the transclude function that is passed to the link function instead.

So I used instead the controller (for the moment) and its $transclude function, as part of the example shown on the $compile documentation:

controller: function($scope, $element, $transclude) {
            var transcludedContent, transclusionScope;

            $transclude(function(clone, scope) {
                $element.append(clone);
                transcludedContent = clone;
                transclusionScope = scope;
            });
        },
Sign up to request clarification or add additional context in comments.

1 Comment

For Angular 2, this is now know as Projections, see stackoverflow.com/a/42019804/2012945
7

This actually is possible with Angular. Directives such as ng-repeat do this. Here is how you do it:

{
    restrict: 'A',
    transclude: true,
    compile: function (tElement, attrs, transclude) {
        return function ($scope) {
            transclude($scope, function (clone) {
                tElement.append(clone);
            });
        };
    }
};

So what's going here? During linking, we are just appending the clone, which is the element we are trying to transclude, into the directive's element. Angular will apply $scope onto the clone element so you can do all the angular goodness inside that element.

1 Comment

It's worth noting that this solution is deprecated in newer versions of Angular. Specifically, the use of the transclude argument passed into the compile function. The currently accepted 'correct' way to do this would be as above, except using the transclude argument in the linking function instead (the 5th argument passed to link).
4

To elaborate on @rob's post...

Transclusion requires that Angular creates an element that is a clone of the content of whatever tag the directive is/lives on... If the content is text, it will wrap it in a span.

This is so it has a DOM element to apply the scope to when $compile is called.

So, basically transclude adds an element for the same reason you can't $compile('plain text here {{wee}}').

Now, you can do something sort of like what you're trying to do with $interpolate, which allows you to apply a scope to bindings in a string like "blah {{foo}}".... but since I'm really not sure what you're trying to do, I can't really give you a specific example.

1 Comment

Boom! It's surprising how long finding a simple answer can take, but this bit right here unlocked it for me. "Transclusion requires that Angular creates an element that is a clone of the content of whatever tag the directive is/lives on... If the content is text, it will wrap it in a span." Thank you, sir!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.