Transclude:'element' demystified

Transclusion is no doubt a very confusing topic in AngularJS. It has two ways of implmentation :

1) transclude: 'true'
2) transclude: 'element'

as far as transclude: 'true' goes, one can grasp it with little effort but transclude: 'element' seems like a lot of work .

So, what thus transclude:'element' do ?

It transcludes the whole element ,i.e, it copies the content inside the directive as well as the directive tag  itself and places one or more copies of it with the correct scope .

The transclusion is achieved via transclusion function unlike the more simpler ng-transclude in case of transclude: 'true' .
For a quick review of transclude:'true' , to achieve transclusion , we included <ng-transclude> wherever we wanted our element to appear inside the 'template' property like :

transclude: 'true',
template: '<some tags here><div ng-transclude><some tags here>',


but when we do  transclude: 'element', we cannot use a template , so we can't  ng-transclude it , so instead, as i just mentioned , we will use transclusion function . Transclusion function is simply a link function , which is returned after compiling the transcluded elements .
To give you an overview of how transclusion is achieved by the browser, the flow is as follows :

var elementsToTransclude = directiveElement.contents();
directiveElement.html(' ');
var transcludeFn= $compile(elementsToTransclude);


So, basically , we are getting the contents of the element containing the directive which has transclusion enabled in the first line and storing them in a variable . Then we are removing the contents  of the element from the DOM . In the third line , we are compiling the variable which has the contents of the element and producing the transclusion function (transcludeFn) .

This function is given as the fifth parameter of 'link' function . It was earlier used in 'compile' function as well but has become obsolete now .

link : function(scope, element, attributes, controller, transcludeFn){}

This Transclude function can accept a scope and a call back function , and this function returns a  clone of the transcluded element instead of the original element  like :

transcludeFn(scope,function(clone){});

This callback function is synchronous not asynchronous . 
Now, its important to understand that this transcludeFn places the transcluded element and the location where you want to place can be achieved by Jquery style DOM traversal and modification . Like if i want to put the transcluded element after the 'element' , which is the second parameter of the link function , i can simply do :

element.after(transcludeFn());


Or i can attach a clone of the transcluded element to the DOM  :

transcludeFn(function(clone){
element.after(clone);
})


In the above two examples of transclusion, the transcluded elements get an inherited scope from the parent scope . Now there is an element of confusion here . The parent scope i am referring to is the original scope not the scope of the directive which has transclusion enabled.
  Let me give an example :
suppose i  have done transclude :element for my custom directive

note that i have isolated the scope of the directive , so in normal sense , anything inside this directive should not inherit from the parent controller scope

app.js :

var app=angular.module('mainModule',[]);
app.controller('mainController',function($scope){
 $scope.Var='Hello I am you father';
});
app.directive('customDirective',function(){
return {
        restrict: 'A',
        scope: {},
        transclude: 'element',
        link: function(scope,element,attrs,controller,transcludeFn){
        transcludeFn(function(clone){
        element.after(clone);
      });
}
}
});


and in my html

index.html:

    <body ng-app="mainModule">
     <div ng-controller="mainController">
       Fathers scope is : {{$id}} 
      <div custom-directive>Hello People Socpe id is : {{$id}} and i am inherited from:                                 {{$parent.$id}} {{Var}}</div>
     </div>
     </body>

{{$id}} is a very neat way to get  the scope id .
{{$parent.$id}} gives the scope id of the parent scope .
i have used {{Var }} in the transcluded element , which is defined only in the parent controller .

So , now in my browser,  i can see :

Fathers scope is : 2
Hello People scope id is: 4 and i am inherited from : 2 Hello i am your father .

Did you see that , did you see that ? That guy is inheriting . 'Var' is being fetched from the parent controller 'mainController' , even though the directive has isolate scope . This behaviour of transclude element is desirable to avoid conflicts between scopes,scope leaking .

Now getting back to the features of transcludeFn , though the default scope of transcluded elements is like the one i mentioned above, you can also provide your own custom scope to it like :

transcludeFn(scope,function(clone){
});


so in the above case, i gave the first parameter as scope, which is also the first parameter of the link function , so you guessed it right , we are assigning it the scope of the custom directive . And since the directive has isolate scope, it will not read any properties from the parent scope and that {{Var}} should not evaluate , since it is not visible now to the transcluded element scope . Right?  Yes ofcourse .

Our view is now :


Fathers scope is : 2
Hello People scope id is: 3 and i am inherited from : 2


Dont get confused by that line 'and i am inherited from : 2', since i used $parent in that and you can access the immediate parent in isolate scopes through $parent , which may sound confusing to some .

Now, lets talk something about the compiling and linking of transcludeFn .

when you do something like :

transcludeFn(scope, function(clone){
element.after(clone) // this is compiled but not linked yet 
}) 


and when you do something like :

var transcluded=transcludeFn();
element.after(transluded) // this is compiled and linked .


So, what is its importance . One is that, if you want to clone the transcluded element several times , like in the case of ng-repeat , you hve to insert the clones into html before linking . They will have individual scopes of their own , again like in ng-repeat ..

Lets further extend our app.js above

var app=angular.module('mainModule',[]);
app.controller('mainController',function($scope){
  $scope.Var='Hello I am you father';
});
app.directive('customDirective',function(){
  return {
    restrict:'A',
    scope:{},     
    transclude: 'element',
    link: function(scope,element,attrs,controller,transcludeFn){
      for (var i = 5; i >= 0; i--) {
      trscluded=transcludeFn(function(clone){
         element.after(clone);
      });
    }
}
}
});

Here you can see we are doing five iterations of the for loop and embedding a clone in our html for each iteration . every clone has its own scope id.

Our view now :

Fathers scope is :2
Hello People Socpe id is : 8 and i am inherited from: 2 Hello I am you father
Hello People Socpe id is : 7 and i am inherited from: 2 Hello I am you father
Hello People Socpe id is : 6 and i am inherited from: 2 Hello I am you father
Hello People Socpe id is : 5 and i am inherited from: 2 Hello I am you father
Hello People Socpe id is : 4 and i am inherited from: 2 Hello I am you father

This happens because we are inserting the html before linking, we wont be able to add clones, if we have already  linked the element, like , if we do

var app=angular.module('mainModule',[]);
app.controller('mainController',function($scope){
  $scope.Var='Hello I am you father';
});
app.directive('customDirective',function(){
  return {
    restrict:'A',
    scope:{},     
    transclude: 'element',
    link: function(scope,element,attrs,controller,transcludeFn){
      for (var i = 5; i >= 0; i--) {
         element.after(transcludeFn());
      }
}
}
});

we will only get a single element in our html :

Our view now :

Fathers scope is :2
Hello People Socpe id is : 8 and i am inherited from: 2 Hello I am you father 

There are many complex implementations of transclde: 'element'  , but as an introduction and understanding to transclude: 'element' , this can get you going .
Cheers .

21 comments:

  1. transcludeFn(scope, function(clone){
    element.after(clone) // this is compiled but not linked yet
    })

    Nice catch, thanks!

    ReplyDelete
  2. You can specify that you want to insert a named transclusion slot, instead of the default slot, by providing the slot name as the value of the ng-transclude or ng-transclude-slot attribute.

    If the transcluded content is not empty (i.e. contains one or more DOM nodes, including whitespace text nodes), any existing content of this element will be removed before the transcluded content is inserted. If the transcluded content is empty (or only whitespace), the existing content is left intact. This lets you provide fallback content in the case that no transcluded content is provided.


    AngularJS Certification Training in Chennai

    ReplyDelete
  3. Great post! I am actually getting ready to across this information, It’s very helpful for this blog.Also great with all of the valuable information you have Keep up the good work you are doing well.
    java training in tambaram | java training in velachery

    java training in omr | oracle training in chennai

    java training in annanagar | java training in chennai

    ReplyDelete
  4. You’ve written a really great article here. Your writing style makes this material easy to understand.. I agree with some of the many points you have made. Thank you for this is real thought-provoking content
    python training in pune
    python online training
    python training in OMR

    ReplyDelete
  5. This is most informative and also this post most user friendly and super navigation to all posts... Thank you so much for giving this information to me.. 
    Devops Training in Chennai

    Devops Training in Bangalore

    Devops Training in pune

    ReplyDelete
  6. Excellent blog, I wish to share your post with my folks circle. It’s really helped me a lot, so keep sharing post like this
    Blueprism training in Chennai

    Blueprism online training

    Blue Prism Training in Pune

    ReplyDelete
  7. I really like the dear information you offer in your articles. I’m able to bookmark your site and show the kids check out up here generally. Im fairly positive theyre likely to be informed a great deal of new stuff here than anyone
    angularjs Training in chennai

    angularjs-Training in chennai

    angularjs Training in chennai

    angularjs-Training in tambaram

    angularjs-Training in sholinganallur

    ReplyDelete
  8. I am really happy with your blog because your article is very unique and powerful for new reader.
    Click here:
    selenium training in chennai
    selenium training in bangalore
    selenium training in Pune
    selenium training in pune
    Selenium Online Training

    https://things-guide.blogspot.com/2014/02/Facebook-Like-Box-with-jQuery-Hover-Effect-for-Blogger.html

    ReplyDelete
  9. I am a regular reader of your blog and being students it is great to read that your responsibilities have not prevented you from continuing your study and other activities. Love

    devops online training

    aws online training

    data science with python online training

    data science online training

    rpa online training

    ReplyDelete
  10. This comment has been removed by the author.

    ReplyDelete
  11. Good to know about the email list business. I was looking for such a service for a long time o grow my local business but the rates that other companies were offering were not satisfactory. thanks
    Ai & Artificial Intelligence Course in Chennai
    PHP Training in Chennai
    Ethical Hacking Course in Chennai Blue Prism Training in Chennai
    UiPath Training in Chennai

    ReplyDelete
  12. Great post! I am actually getting ready to across this information, It’s very helpful for this blog.Also great with all of the valuable information you have Keep up the good work you are doing well.
    amazon web services aws training in chennai

    microsoft azure training in chennai

    workday training in chennai

    android-training-in chennai

    ios training in chennai

    ReplyDelete
  13. Whoa! I’m enjoying the template/theme of this website. It’s simple, yet effective. A lot of times it’s very hard to get that “perfect balance” between superb usability and visual appeal. I must say you’ve done a very good job with this.
    IELTS Coaching in chennai

    German Classes in Chennai

    GRE Coaching Classes in Chennai

    TOEFL Coaching in Chennai

    Spoken english classes in chennai | Communication training

    ReplyDelete