4

I am reading angular transclusion which some examples from this page: http://www.c-sharpcorner.com/UploadFile/17e8f6/transclusion-in-custom-angularjs-directive/

This page gave useful examples. However I wonder when to use transclusion. The original example:

<user-post user-name="{{user.Name}}" post-details="post" ng-repeat="post in user.Posts">
    <user-likes post-likes-count="{{post.Likes.length}}"></user-likes>
</user-post>

This is HTML template of userPost directive:

<div class="panel panel-default panel-primary">
    <div class="panel-heading">
        <h5><strong>{{userName}}</strong></h5>
    </div>
    <div class="panel-body">
        <img ng-src="{{postDetails.Content}}" alt="Image" class="img-responsive" />
    </div>
    <div class="panel-footer" ng-transclude>
    </div>
</div>

But I can rewrite it without transclusion:

<div class="panel panel-default panel-primary">
    <div class="panel-heading">
        <h5><strong>{{userName}}</strong></h5>
    </div>
    <div class="panel-body">
        <img ng-src="{{postDetails.Content}}" alt="Image" class="img-responsive" />
    </div>
    <div class="panel-footer">
        <user-likes post-likes-count="{{likeCount}}"></user-likes>
    </div>
</div>

I want to know the best cases to use transclusion?

Leo Vo
  • 9,980
  • 9
  • 56
  • 78
  • I'm going to make an attempt to try to explain transclusion, it will take a while to get the answer right before posting though. – Claies Jan 25 '16 at 05:47

3 Answers3

4

To understand Transclusion, and when/how to use it, you have to understand a bit about the DOM you are trying to represent, and how angular directives play a role in the DOM.

Specifically, this is related to directives which are Elements (restrict: 'E'). Let's take the most basic HTML for a directive we can possibly take:

.directive('myDirective', function(){
  return {
    restrict: 'E',
    template: '<div></div>' +
              '<div></div>'
  };

Usage:

<my-directive>Some Content</my-directive>

Now, here is the question that is the key to understanding transclusion:

Where does Some Content end up?

In this case, Some Content has nowhere to go. When the directive is injected into the DOM, it is replaced by the template. The template doesn't know about Some Content, and has no place to put it, and therefore it will not be output in the final DOM.

Now, adding transclusion, we can solve this problem.

.directive('myDirective', function(){
  return {
    restrict: 'E',
    transclude: true,
    template: '<div></div>' +
              '<ng-transclude></ng-transclude>'
  };

Now, when the DOM is compiled, Some Content has a place to go. Transclusion allows the inner content of the element to be placed in the template, in the spot where the ng-transclude directive exists.

Now, transclusion can get very tricky, especially when you have a directive nested in the inner content of another directive, but it can be very powerful. However, if your directive will not have content inside the element in your markup, transclusion isn't necessary. There are many more ways to make use of transclusion, but at their core, they all ultimately come back to this concept.

Claies
  • 22,124
  • 4
  • 53
  • 77
1

You would use transclude to inject bunch of html into your directive. The idea of creating directive is so that you have reusable bunch of code that you can use within various parts of your application. Transclusion allows you to further customize the content of your directive from the template.

Following your example, the idea to create user-post directive with transclusion would be that your user post might not have exact same content throughout different part of your application.

If you notice in template 2, I can add some extra HTML to accommodate different content in the directive.

Template 1:

<user-post user-name="{{user.Name}}" post-details="post" ng-repeat="post in user.Posts">
    <user-likes post-likes-count="{{post.Likes.length}}"></user-likes>
</user-post>

Template 2:

<user-post user-name="{{otherUser.Name}}" post-details="post" ng-repeat="post in otherUser.Posts">
    <user-likes post-likes-count="{{post.Likes.length}}"></user-likes>

    // this directive might loop through post.comments and display a list
    // of comments on the post.
    <user-comments post-comments="{{post.comments}}"></user-comments>

    <p>some other customize html</p>
</user-post>

Now I have 2 user posts in my application. One has posts and user likes and the other has posts with user likes and comments.

Subash
  • 7,098
  • 7
  • 44
  • 70
1

There are two separate features provided by the same API: Regular transclusion and Element transclusion.

For regular transclusion, the basic use case is: You want to include or replace some content from one template to another. There are two ways to do this using regular transclusion: the ngTransclude directive and the Transclusion Function.

The following is from Eric Greene that I thought was a good explanation for Regular transclusion with code, Understanding Transclusion.

ngTransclude

HTML Code

<div foo>
  Some Content Here
</div>

JavaScript Code

.directive("foo", function() {
  // returns the Directive Definition Object
  return {
    transclude: true,
    template: "<div>the template</div><div ng-transclude></div>"
  };
})

The result of the using the ngTransclude directive is the following HTML output. HTML Code

<div foo>
  <div>the template</div>
  <div ng-transclude>Some Content Here</div>
</div>

Transclude Function

The second method to do transclusion is to use the transclude function provided in the post-link function of a directive. In the code below, the link property points to the post-link function. Unless a pre-link function is specifically provided, all references to a link function refer to the post-link function. Review the code below to see how the same goal is achieved with a transclude function:

HTML Code

<div foo>
  Some Content Here
</div>
JavaScript Code
.directive("foo", function() {
  return {
    template: "<div>the template</div>",
    transclude: true,
    link: function(scope, element, attrs, ctrl, transclude) {
      transclude(function(clone) {
        element.append(clone);
      });
    }
  };
})

The transclude function is passed an argument that is a callback function used to manipulate the cloned element. In the code above, the callback function will receive the cloned content as a parameter, then the function adds the cloned content to the DOM. The execution of the of link function results in the following HTML output:

HTML Code

<div foo>
  <div>
      the template
      <div>Some Content Here</div>
  </div>
</div>

However, for Element transclusion, I would like to expand upon his answer because his explanation seemed confusing and incomplete for use cases. On Tero Parviainen’s blog “ A Guide to Transclusion in Angularjs” he gets to the root of the element transclusion, mainly to defer linking and attachment for some part of the UI.

To quote him:

Understanding Element Transclusion

Let's now turn our attention to an alternative way you can use transclusion: Doing element transclusion with transclude: 'element'.

One use case for element transclusion is when you want to defer the linking and attachment for some part of your UI until something happens. Since element transclusion essentially removes part of the DOM and makes it available for you to put back using the transclusion function, you have control into when that happens.

For example, you might want to only link and attach part of the DOM after some condition on the parent scope becomes true: What we have here is essentially a simplified version of ngIf. If you look at the source code of ngIf, you should now be able to understand what it's doing: It's using element transclusion to conditionally link this part of the DOM only when the condition expression is true. This also explains why you see HTML comments in your DOM tree whenever ngIf is used. They're inserted by element transclusion.

This was something I would see in the Element tab in Chromes Dev Tools when viewing the source on a page, but didn’t understand where it was coming from. I’ve also see it with ng-repeat.

Repeated Rendering with Element Transclusion. Another use case for element transclusion is if you want to link and attach part of the DOM several times.

For example, you could have a directive that iterates over an array and renders the DOM subtree again for each item in the array, also making each item available on the scope. This can all be done when we combine the element transclusion mechanism with the clone attach function, because then we can link a new clone for the DOM for each item: This is essentially a (hugely) simplified version of ngRepeat. The built-in ngRepeatdirective also uses element transclusion, though studying its source code is not quite as easy as with ngIf because of the various ways you can use ngRepeatand all the optimizations it contains. At the heart of it though, it's just an application of element transclusion and the clone attach function.

I felt like Tero's coverage on the use of Transclusion by element was better than I could have said it.

James Drinkard
  • 15,342
  • 16
  • 114
  • 137