Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Transclude in AngularJS without adding new element

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!

like image 850
rob Avatar asked Oct 16 '13 18:10

rob


3 Answers

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;
            });
        },
like image 80
edmundo096 Avatar answered Oct 22 '22 17:10

edmundo096


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.

like image 22
Vakey Avatar answered Oct 22 '22 18:10

Vakey


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.

like image 32
Ben Lesh Avatar answered Oct 22 '22 18:10

Ben Lesh