Replace a directive by another one without compiling children nodes twice
<p>In a project, I have built a directive <em>aDir</em> that is replaced by another one <em>bDir</em> using $compile in its post-link function. It creates a "shortcut" for <em>bDir</em> which is useful because <em>bDir</em> has many arguments and I use it all accross the project.</p>

<p>In my templates:</p>

<pre><code>&lt;p&gt;
    &lt;button a-dir&gt;&lt;/button&gt;
&lt;/p&gt;
</code></pre>

<p>Which is compiled in:</p>

<pre><code>&lt;p&gt;
    &lt;button b-dir arg1="" arg2="" ... &gt;&lt;/button&gt;
&lt;/p&gt;
</code></pre>

<p>It works great with a code like this:</p>

<pre><code>function aDir($compile){
  return {
    restrict: 'A',
    link: function(scope, iElem, iAttrs){
      iElem.attr('b-dir', '');
      iElem.attr('arg1', '');
      iElem.attr('arg2', '');
      [...]
      iElem.removeAttr('a-dir'); // To avoid infinite loop
      $compile(iElem)(scope);
    }
  }
}
</code></pre>

<p>Nevertheless, if the element on which <em>aDir</em> is apply has children, they will be compiled twice. Once by the <code>$compile</code> function initiated by Angular and once by the <code>$compile</code> function I call in <em>aDir</em> post-link.</p>

<p>Consider <a href="http://plnkr.co/edit/SXfkVyaQ0JrwEAqI4xov?p=info" rel="nofollow">this plunker</a>. This is the HTML:</p>

<pre><code>&lt;outer-level&gt;
  &lt;p replace-by-another&gt;
    &lt;inner-level&gt;Hello World!&lt;/inner-level&gt;
  &lt;/p&gt;
&lt;/outer-level&gt;
</code></pre>

<p><em>replaceByAnother</em> is replaced by the directive called <em>another</em>. <em>outerLevel</em> and <em>innerLevel</em> are directives that does nothing but log in the console when their compile, pre and post link functions are called.</p>

<p>The console logs are:</p>

<pre><code>outerLevel: compile
replaceByAnother: compile
innerLevel: compile
outerLevel: pre link
replaceByAnother: pre link
innerLevel: pre link
innerLevel: post link
replaceByAnother: post link
    another: compile
    innerLevel: compile
    another: pre link
    innerLevel: pre link
    innerLevel: post link
    another: post link
outerLevel: post link
</code></pre>

<p>So we have two calls of compile, pre link and post link functions of <em>innerLevel</em>. In my case that's totally ok but I'm interested in understanding entirely what <code>$compile</code> does and if such a behavior could be avoided.</p>

<p>I tried a couple of things by defining a compile function in <em>replaceByAnother</em> directive but I just manage to change the order of execution and not to compile just once <em>innerLevel</em> directive:</p>

<p><a href="http://plnkr.co/edit/ZnBRaskb1WPkRZv36giS" rel="nofollow">http://plnkr.co/edit/ZnBRaskb1WPkRZv36giS</a></p>

<pre><code>function replaceByAnother($compile){
  return {
    restrict: 'A',
    compile: function(tElem, tAttrs){
      console.log('replaceByAnother: compile');
      tElem.attr('another', '');
      tElem.removeAttr('replace-by-another');
      var anotherLinkFunc = $compile(tElem);

      return {
        pre: function(scope, iElem, iAttrs){
          console.log('replaceByAnother: pre link');
        },
        post: function(scope, iElem, iAttrs){
          console.log('replaceByAnother: post link');
          anotherLinkFunc(scope);
        }
      }
    }
  }
}
</code></pre>

<p>And the results:</p>

<pre><code>outerLevel: compile
replaceByAnother: compile
  another: compile
  innerLevel: compile
innerLevel: compile
outerLevel: pre link
replaceByAnother: pre link
innerLevel: pre link
innerLevel: post link
replaceByAnother: post link
  another: pre link
  innerLevel: pre link
  innerLevel: post link
  another: post link
outerLevel: post link
</code></pre>

<p>Do you have any idea?</p>

<p><strong>SOLUTION</strong></p>

<p>With the answers of @georgeawg and @AndrésEsguerra, I found a satisfacting solution:</p>

<ul>
<li>Use <code>terminal: true</code> and a high priority to prevent Angular from compiling twice children nodes.</li>
<li>Modify the template element in the compile function and call <code>$compile</code> on it. Store the output of <code>$compile</code>.</li>
<li>Call this output in the link function to bind the template element to the scope.</li>
</ul>

<hr>

<pre><code>function replaceByAnother($compile){
  return {
    restrict: 'A',
    terminal: true,
    priority: 100000,
    compile: function(tElem, tAttrs){
      tElem.attr('another', '');
      tElem.removeAttr('replace-by-another');
      var anotherLinkFunc = $compile(tElem);

      return {
        pre: function(scope, iElem, iAttrs){
          // pre link
        },
        post: function(scope, iElem, iAttrs){
          anotherLinkFunc(scope, function cloneAttachedFn(clone) {
              iElem.replaceWith(clone);
          });
        }
      }
    }
  }
}
</code></pre>