Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why use terminal: true instead of removing lower priority directives?

Related: How to understand the `terminal` of directive?

Why would someone set terminal: true and a priority on a directive rather than simply removing the lower priority directives? For example, they could write:

<tag directive-1 directive-2 directive-3></tag>

... and they could add priority: 100 and terminal: true to directive-3, so that only directive-3 would be applied to the element.

Why wouldn't someone instead change their template to:

<tag directive-3></tag>

Perhaps it simplifies the code in some cases by allowing multiple directives to be added to an element and offloading the work of deciding which ones to actually apply to Angular?

Thanks.

like image 280
Anonymous Avatar asked Sep 23 '13 21:09

Anonymous


2 Answers

Setting the priority and terminal options is not about erasing directives, it's declaring the order of compilation and linking. Everybody points to ng-repeat as the prime example of priority + terminal + transclude, so I'll give a extremely simplified version of ng-repeat:

app.directive('fakeRepeat', function($log) {
  return {
    priority: 1000,
    terminal: true,
    transclude: 'element',
    compile: function(el, attr, linker) {
      return function(scope, $element, $attr) {
        angular.forEach(scope.$eval($attr.fakeRepeat).reverse(), function(x) {
          var child = scope.$new();
          child[attr.binding] = x;
          linker(child, function(clone) {
            $element.after(clone);
          })
        })
      }
    }
  }
});

The fake repeat directive can be used as so:

<ul>
  <li fake-repeat="things" binding="t" add-letter>{{ t }}</li>
<ul>

Now extra directives can be attached to the same li that contains fake repeat, but their priority + terminal options will determine who gets compiled first, and when linking happens. Normally we expect the li element to be cloned and for and for the add-letter directive to be copied for each binding t, but that will only happen if add-letter has a lower priority than fake-repeat.

Case 1: add-letter priority is 0

Linking is executed for each li generated.

Case 2: add-letter priority is 1001

Linking is executed before fake-repeat and thus before the transclude happens.

Case 3: add-letter priority is 1001 and terminal is true

Compilation stops before fake-repeat so the directive is never executed.

Here is a plunker with console logging for further exploration.

like image 186
mortalapeman Avatar answered Sep 24 '22 19:09

mortalapeman


I believe terminal was created to work with directives that use transclusion or directives that are meant to replace all of an element's content.

If an element uses terminal then it does not want applicable directives to compile during the initial directive collection. The initial collection is triggered either by Angular's bootstrap process or a manual $compile. Just because the terminal directive does not want the lower priority directives to compile, does not mean that it does not want the directives to run later, which is why transclude is a perfect use case.

The contents are compiled and stored as a link function that can be evaluated against any scope at any time. This is how ngRepeat, and ngIf perform.

When writing a directive that uses transclusion maybe consider if it should use terminal as well.

I don't believe it's very useful when using it with directives that don't use transclude.

like image 20
joshkurz Avatar answered Sep 24 '22 19:09

joshkurz