Understanding the transclude option of directive definition?

86,934

Solution 1

Consider a directive called myDirective in an element, and that element is enclosing some other content, let's say:

<div my-directive>
    <button>some button</button>
    <a href="#">and a link</a>
</div>

If myDirective is using a template, you'll see that the content of <div my-directive> will be replaced by your directive template. So having:

app.directive('myDirective', function(){
    return{
        template: '<div class="something"> This is my directive content</div>'
    }
});

will result in this render:

<div class="something"> This is my directive content</div> 

Notice that the content of your original element <div my-directive> will be lost (or better said, replaced). So, say good-bye to these buddies:

<button>some button</button>
<a href="#">and a link</a>

So, what if you want to keep your <button>... and <a href>... in the DOM? You'll need something called transclusion. The concept is pretty simple: Include the content from one place into another. So now your directive will look something like this:

app.directive('myDirective', function(){
    return{
        transclude: true,
        template: '<div class="something"> This is my directive content</div> <ng-transclude></ng-transclude>'
    }
});

This would render:

<div class="something"> This is my directive content
    <button>some button</button>
    <a href="#">and a link</a>
</div>. 

In conclusion, you basically use transclude when you want to preserve the contents of an element when you're using a directive.

My code example is here. You could also benefit from watching this.

Solution 2

I think it is important to mention changes in the above behaviour in new version of AngularJS. I spent one hour trying to achieve above results with Angular 1.2.10.

Contents of the element with ng-transclude are not appended but completely replaced.

So in the above example, what you would achieve with 'transclude' would be:

<div class="something">
    <button>some button</button>
    <a href="#">and a link</a>
</div>

and not

<div class="something"> This is my directive content
    <button>some button</button>
    <a href="#">and a link</a>
</div>

Thanks.

Solution 3

What TechExplorer says is true but you can have both contents by including in your template a simple container tag (like div or span) with the ng-transclude attribute. This means that the following code in your template should include all content

<div class="something"> This is my directive content <div class="something" ng-transclude></div></div>

Solution 4

The Updated AngularJS 1.6.6 documentation now has a better explanation.

Transclude is Used to Create a Directive that Wraps Other Elements

Sometimes it's desirable to be able to pass in an entire template rather than a string or an object. Let's say that we want to create a "dialog box" component. The dialog box should be able to wrap any arbitrary content.

To do this, we need to use the transclude option. Refer to the example below.


script.js

angular.module('docsTransclusionExample', [])
.controller('Controller', ['$scope', function($scope) {
  $scope.name = 'Tobias';
}])
.directive('myDialog', function() {
  return {
    restrict: 'E',
    transclude: true,
    scope: {},
    templateUrl: 'my-dialog.html',
    link: function(scope) {
      scope.name = 'Jeff';
    }
  };
});

index.html

<div ng-controller="Controller">
  <my-dialog>Check out the contents, {{name}}!</my-dialog>
</div>

my-dialog.html

<div class="alert" ng-transclude></div>

Compiled Output

<div ng-controller="Controller" class="ng-scope">
  <my-dialog class="ng-isolate-scope"><div class="alert" ng-transclude="">Check out the contents, Tobias!</div></my-dialog>
</div>

Transclude makes the contents of a directive with this option have access to the scope outside of the directive rather than inside.

This is illustrated in the previous example. Notice that we've added a link function in script.js that redefines name as Jeff. Ordinarily, we would expect that {{name}} would be Jeff. However, we see in this example that the {{name}} binding is still Tobias.

Best Practice: only use transclude: true when you want to create a directive that wraps arbitrary content.

Solution 5

From Wiki:

"In computer science, transclusion is the inclusion of part or all of an electronic document into one or more other documents by reference."

I'd like to add another use for transclusion, and that is that it changes the execution order of the compile and link functions of parent and child directives. This can be useful when you want to compile the child DOM before the parent DOM as the parent DOM perhaps depends on the child DOM. This article goes more in depth and clarifies it very well!

http://www.jvandemo.com/the-nitty-gritty-of-compile-and-link-functions-inside-angularjs-directives-part-2-transclusion/

Share:
86,934
Freewind
Author by

Freewind

A programmer ([email protected])

Updated on December 05, 2020

Comments

  • Freewind
    Freewind over 3 years

    I think this is one of the hardest concept for me to understand with angularjs's directive.

    The document from http://docs.angularjs.org/guide/directive says:

    transclude - compile the content of the element and make it available to the directive. Typically used with ngTransclude. The advantage of transclusion is that the linking function receives a transclusion function which is pre-bound to the correct scope. In a typical setup the widget creates an isolate scope, but the transclusion is not a child, but a sibling of the isolate scope. This makes it possible for the widget to have private state, and the transclusion to be bound to the parent (pre-isolate) scope.

    • true - transclude the content of the directive.
    • 'element' - transclude the whole element including any directives defined at lower priority.

    It says transclude typically used with ngTransclude. But the sample from the doc of ngTransclude doesn't use ngTransclude directive at all.

    I'd like some good examples to help me understand this. Why do we need it? What does it solve? How to use it?

    • Sandy
      Sandy over 6 years
      FYI...link is working at least now
  • Tarjei Romtveit
    Tarjei Romtveit over 10 years
    Seems like they have changed the the functionality a bit. At-least in version >= 1.2.9. The content from the template is not added to the rendered content. See @TechExplorer's answer below
  • Matheus
    Matheus over 9 years
    That was the key information that was missing on the other answers
  • Mark Rajcok
    Mark Rajcok over 9 years
    For more information about the changed behavior in Angular 1.2, see change eed299a.
  • freeall
    freeall about 9 years
    A very, very good answer. Way above the normal. You have good examples, and your "this is my directive content" made it very easy to read in the rendered version. I don't understand why Angular has to use complex terminology and concepts and then don't include easy-to-understand examples like yours. +2
  • Simon Green
    Simon Green about 9 years
    Does anyone know if the transcluded content can refer to the directive's isolate scope fields? It says above that the transclusion is a sibling, not a child, of the isolate scope...so I'm assuming it can't - but was wondering if someone could confirm or let me know if it is possible
  • odiseo
    odiseo almost 9 years
    @UladzimirHavenchyk thanks, they moved the video to other place. I fixed the link accordingly.
  • Royi Namir
    Royi Namir over 8 years
    (1.4.8)this is not true. in your first code <div my-directive="" stays.
  • pdorgambide
    pdorgambide over 8 years
    The transclusion has two main objetives render content and also made available to the directive inner content the outer scope (controller app scope). The advantage of transclusion is that the linking function receives a transclusion function which is pre-bound to the correct scope. To render the inner content of the directive mantaining the directive scope you can use $compile. Using $compile
  • Dan Hodson
    Dan Hodson over 8 years
    @odiseo, could you please write ALL the Angular docs in plain, simple to understand English like this! + many 1's.
  • BeingSuman
    BeingSuman over 7 years
    This answer adds so much information. ng-transclude is the attribute which acts as place holder, inside which transcluded content will be placed.
  • Sohan Soni
    Sohan Soni over 5 years
    Awesome explanation in very simple words ! Thank you
  • CoronaVirus
    CoronaVirus over 5 years
    well explained.
  • autobahn
    autobahn over 4 years
    In the last example, shouldn’t the button and the link be outside the div, since the ng-transclude tag comes after the closing of the div tag in the template definition?