70
votes

From the directive Angular docs, I see the compile function has 3 parameters, one of which is transclude. The only explanation the docs provide is:

transclude - A transclude linking function: function(scope, cloneLinkingFn).

I'm trying to understand what exactly you would do in the clone linking function. I don't even know what parameters get passed into it. I found one example that has one parameter called clone that appears to be an HTML element. Are there other parameters available? Which HTML element is this exactly? I'm also looking at probably using transclude: 'element' in my directive. Do the answers to those questions change when using 'element' instead of true?

I'm understanding transclusion with the simple examples, but I can't to seem to find more complex examples, especially with transclude: 'element'. I'm hoping someone can provide a more thorough explanation about all this. Thanks.

1

1 Answers

55
votes

EDIT: Completely and totally changing my answer and marking this as "Community Wiki" (meaning no points for me) as I was outright wrong when I answered this

As @Jonah pointed out below, here is a really good article on the compile option of directives and using the transclusion function

The basic idea is the compile function should return a linking function. You can use the transclusion function provided inside the linking function to take a clone of the transcluded DOM element, compile it, and insert it wherever it needs to be inserted.

Here is a better example I've pulled out of my butt on Plunker

The idea of the compile function is it gives you a chance to programmatically alter the DOM elements based on attributes passed BEFORE the linking function is created and called.

// a silly directive to repeat the items of a dictionary object.
app.directive('keyValueRepeat', function ($compile){
  return {
    transclude: true,
    scope: {
      data: '=',
      showDebug: '@'
    },
    compile: function(elem, attrs, transclude) {

      if(attrs.showDebug) {                
        elem.append('<div class="debug">DEBUG ENABLED {{showDebug}}</div>');
      }

      return function(scope, lElem, lAttrs) {
        var items = [];
        console.log(lElem);
        scope.$watch('data', function(data) {

          // remove old values from the tracking array
          // (see below)
          for(var i = items.length; i-- > 0;) {
            items[i].element.remove();
            items[i].scope.$destroy();
            items.splice(i,1);
          }

          //add new ones
          for(var key in data) {

            var val = data[key],
                childScope = scope.$new(),
                childElement = angular.element('<div></div>');

            // for each item in our repeater, we're going to create it's
            // own scope and set the key and value properties on it.
            childScope.key = key;
            childScope.value = val;

            // do the transclusion.
            transclude(childScope, function(clone, innerScope) {
              //clone is a copy of the transcluded DOM element content.
              console.log(clone);

              // Because we're still inside the compile function of the directive,
              // we can alter the contents of each output item
              // based on an attribute passed.
              if(attrs.showDebug) {                
                clone.prepend('<span class="debug">{{key}}: {{value}}</span>');
              }

              //append the transcluded element.
              childElement.append($compile(clone)(innerScope));
            });

            // add the objects made to a tracking array.
            // so we can remove them later when we need to update.
            items.push({
              element: childElement,
              scope: childScope
            });

            lElem.append(childElement);
          }
        });
      };
    }
  };
});