What are the benefits of a directive template function in Angularjs?

37,607

Solution 1

The compilation function can be used to change the DOM before the resulting template function is bound to the scope.

Consider the following example:

<div my-directive></div>

You can use the compile function to change the template DOM like this:

app.directive('myDirective', function(){
  return {

    // Compile function acts on template DOM
    // This happens before it is bound to the scope, so that is why no scope
    // is injected
    compile: function(tElem, tAttrs){

      // This will change the markup before it is passed to the link function
      // and the "another-directive" directive will also be processed by Angular
      tElem.append('<div another-directive></div>');

      // Link function acts on instance, not on template and is passed the scope
      // to generate a dynamic view
      return function(scope, iElem, iAttrs){

        // When trying to add the same markup here, Angular will no longer
        // process the "another-directive" directive since the compilation is
        // already done and we're merely linking with the scope here
        iElem.append('<div another-directive></div>');
      }
    }
  }
});

So you can use the compile function to change the template DOM to whatever you like if your directive requires it.

In most cases tElem and iElem will be the same DOM element, but sometimes it can be different if a directive clones the template to stamp out multiple copies (cf. ngRepeat).

Behind the scenes, Angular uses a 2-way rendering process (compile + link) to stamp out copies of a compiled piece of DOM, to prevent Angular from having to process (= parse directives) the same DOM over and over again for each instance in case the directive stamps out multiple clones resulting in much better performance.

Hope that helps!


ADDED AFTER COMMENT:

The difference between a template and compile function:

Template function

{
    template: function(tElem, tAttrs){

        // Generate string content that will be used by the template
        // function to replace the innerHTML with or replace the
        // complete markup with in case of 'replace:true'
        return 'string to use as template';
    }
}

Compile function

{
    compile: function(tElem, tAttrs){

        // Manipulate DOM of the element yourself
        // and return linking function
        return linkFn(){};
    }
}

The template function is called before the compile function is called.

Although they can perform almost identical stuff and share the same 'signature', the key difference is that the return value of the template function will replace the content of the directive (or the complete directive markup if replace: true), while a compile function is expected to change the DOM programmatically and return a link function (or object with pre and post link function).

In that sense you can think of the template function as some kind of convenience function for not having to use the compile function if you simply need to replace the content with a string value.

Hope that helps!

Solution 2

One of the best uses of the template function is to conditionally generate a template. This allows you to automate the creation of a template based on an attribute or any other condition.

I have seen some very large templates that use ng-if to hide sections of the template. But instead of placing everything into the template and using ng-if, which can cause excessive binding, you can remove sections of the DOM from the output of the template function that will never be used.

Let's say you have a directive that will include either sub-directive item-first or item-second. And the sub-directive will not ever change for the life of the outer directive. You can adjust the output of the template, prior to the compile function being called.

<my-item data-type="first"></my-item>
<my-item data-type="second"></my-item>

And the template string for these would be:

<div>
  <item-first></item-first>
</div>

and

<div>
  <item-second></item-second>
</div>

I agree that this is an extreme simplification, But I have some very complicated directives and the outer directive needs to display one of, about, 20 different inner directives based on a type. Instead of using transclude, I can set the type on the outer directive and have the template function generate the correct template with the correct inner directive.

That correctly formatted template string is then passed on to the compile function, etc.

Share:
37,607
Pol1na
Author by

Pol1na

Updated on November 15, 2020

Comments

  • Pol1na
    Pol1na over 3 years

    According to the documentation a template can be a function which takes two parameters, an element and attributes and returns a string value representing the template. It replaces the current element with the contents of the HTML. The replacement process migrates all of the attributes and classes from the old element to the new one.

    The compile function deals with transforming the template DOM. It takes three parameters, an element, attributes and transclude function. The transclude parameter has been deprecated. It returns a link function.

    It appears that a template and a compile functions are very similar and can achieve the same goal. The template function defines a template and compile function modifies the template DOM. However, it can be done in the template function itself. I can't see why modify the template DOM outside the template function. And vice-versa if the DOM can be modified in the compile function then what's the need for a template function?

  • Pol1na
    Pol1na over 10 years
    Thanks for trying to answer my question but it's not what I was asking. My question was about the benefits of a template: function(element, attrs) { return "<div></div>"; } function compared to the compile function.
  • jvandemo
    jvandemo over 10 years
    I updated the answer. Feel free to let me know if you need more info. Thanks!
  • Pol1na
    Pol1na over 10 years
    Would you say then that one should use either template or compile function? Because using both seems a bit redundant.
  • jvandemo
    jvandemo over 10 years
    Yes, that's right. In most cases one of the two will suffice. Which one depends on whether you want to return a string (template function) or manipulate the DOM programmatically (compile function).
  • Pol1na
    Pol1na over 10 years
    Ok I think your answer combined with charlietfl's comments draws a good use case of the template function for me. Thanks!
  • TheChrisONeil
    TheChrisONeil over 8 years
    Another difference is the returned link function in the compile function cannot include the isolate scope.
  • jose.angel.jimenez
    jose.angel.jimenez almost 8 years
    This is the right answer and should be also the accepted one. As a proof, notice that Angular 1.5 and 2 are heavily recommending the use of component instead of directive. The component won't let you define a custom compile() or link() functions, but will and encourage to use a template function for cases likes the one described by the OP.
  • Dimitry K
    Dimitry K about 5 years
    What if you set up dynamically data-type as in <my-item data-type="{{ $ctrl.someItem.dataType}}"></my-item> ? Looks like template function will see literal values of the attributes eg. tAttr['data-type'] == '{{ $ctrl.someItem.dataType}}' instead of tAttr['data-type'] == 'first' :(
  • Intervalia
    Intervalia about 5 years
    It has been too long since I used AngularJS. But I seem to remember that you can call the function $parse to get the value out of $ctrl.someItem.dataType. At least I think it was $parse
  • Dimitry K
    Dimitry K about 5 years
    Hmm... Thank you it is definitely step in the right direction. It allows to convert string expression into function, but the function requires two parameters (context, locals) from which it will be resolving the actual expression. And within template() function the $scope/context of the directive is not visible....