0

I've found that angular doesn't take advantage of order the directives are defined in, instead it uses static priority field. It does not suite well for all cases.

Example:

<div ng-if="items.length < 50" ng-repeat="item in items"></div>

<div ng-repeat="item in items" ng-if="items.length > 50"></div>

These two lines of code can have different meaning.

Case 1) check if amount of items less than 50, ng-repeat 10000 items

Case 2) ng-repeat 10000 items, then on each element check if amount of items is less than 50

If ng-if had higher priority the first line of code would allow obviously very important optimization...

Obviously there is not much reason for using static "priority" field compared to prioritizing directives with order they are defined in, so my question is:

What steps should be taken to approve this idea and have it implemented?

(I was never looking into angularjs source code, some external help is required to point-out the places that need to be changed in order for providing the subject approach, I would be thankful for any external help in this direction)

Thank you in advance!


Edit

Here's a jsFiddle showing that ng-if is getting executed 20000 times for an array of 10000 items, each ng-if creates a scope which doubles the problem...

http://jsfiddle.net/u840t0dh/17/

Lu4
  • 13,853
  • 12
  • 68
  • 122
  • huh? how can array with length less than 50 repeat 10000 times? Do you really want to have to keep track of a list of orders that have to be adhered to? That just adds more complication and would make testing and bugs a real chore – charlietfl Jan 11 '15 at 04:53
  • Nope, it would have mend: repeat only if array size less than 50 – Lu4 Jan 11 '15 at 05:40
  • well that's not how ng-if works. If there are 51 those elements won't exist in the DOM ...any of them – charlietfl Jan 11 '15 at 05:44
  • The elements would not exist either case, the problem is that before getting checked with `ng-if` they will get repeated 10000 times and then each element checked for `items.length < 50`, and only then they will get removed. And it will happen only because `ng-repeat` has `priority` equal to 1000 and `ng-if` has priority `450`. By not having an ability to affect the order of directive "execution" can cause problems, does that make things clear? – Lu4 Jan 11 '15 at 05:48
  • First `ng-repeat` is executed, then `ng-if` is executed, how come they will not get created if `ng-repeat` is executed first? – Lu4 Jan 11 '15 at 05:51
  • Here's a jsFiddle for you @charlietfl http://jsfiddle.net/u840t0dh/17/ the elements get created 10000 times by `ng-repeat` and then are being removed by `ng-if`, if you look into the console you will notice huge amount of angular comments, each of them represents $scope created by `ng-if` does that seem like a nice behaviour for you? Can you spot the problem now? – Lu4 Jan 11 '15 at 06:35
  • Your question isn't about a change to angular behavior. Go start a thread on the angular forums or write an issue in their github, but this isn't the place for that discussion. – Steve Mitcham Jan 28 '15 at 13:21

2 Answers2

4

I can't find a reason on why angular doesn't take advantage of order the directives are defined in, rather than using static priority field, it does not suite well for all cases.

The idea of angular directives is to extend browser built-in markup languages (tags, attributes, ...). I believe there is no such feature in standard browser markup. I want to stress that directives in angular is declarative programming.

For the problem you pointed out, it looks to me like imperative programming. It's like you're coding your logic in the page with an if and a loop. But in your case, it does not make much sense to use them on the same element => it's very similar to writing if and for loop on the same line in any imperative programming languages (c++, c#, java,...). It makes more sense to write it like this if you follow imperative programming mindset:

<div ng-if="items.length > 50">
    <div ng-repeat="item in items"></div>
</div>

I agree that sometimes in order to write UI rendering code, we have to write a bit like imperative programming but it could be kept to the bare minimum. Even in those case, if you follow the mindset of imperative programming (like the one I pointed out), there should not be a problem.

Therefore, the problem you pointed could be a problem but actually not a big problem compared to the advantages that we gain from declarative markups.

Declarative programming and imperative programming has their own pros and cons:

For the case of directives to extend "markup", it makes more sense with declarative programming which is the way angular directives were designed (markup is really declarative)

The point of declarative programming is about telling "what" you want, not "how" to do it. That comes with the benefit: it's simpler and easier to understand.

From MSDN

In fact, the ease of understanding code—even in an unfamiliar context—is one of the principal benefits of a declarative style.

The most notable benefit of declarative programming is that programs specify “what” we want to get as the result rather than “how”. As a result, it is more important to understand the domain that we’re working with than every detail of the language.

With your suggestion, it's more about "how" you want to get a task done. The downside of your suggestion is that when we use directives, we need to understand how they work "internally".

For example: when you use many directives on the same element, the person who uses the directives need to understand what is the order to put the directives (that's should be the concern of the person who implements the directives). That's something declarative programming tries to avoid because it's complex for users of the directives

Community
  • 1
  • 1
Khanh TO
  • 46,783
  • 12
  • 97
  • 112
  • What feature do you refer to by "I believe there is no such feature in standard browser markup"? – Lu4 Feb 02 '15 at 00:15
  • Khan from my perspective it doesn't break declarative programming paradigm. My proposition refers to already defined concepts. I'm suggesting to make them more flexible. The directive order already defines the notion of priority, so having one inside directive is an over-engineering. So my proposition makes angular more simple and flexible. What are the gains if it will remain bulky and crusty but "declarative" according to your understanding? And it's "unfair" to refer to "declarative programming" since there is no universal measure for code being declarative enough for this or that purpose – Lu4 Feb 02 '15 at 00:59
  • @Lu4: The point of declarative programming is about telling "what" you want, not "how" you do it. With your suggestion, it's more about "how" you want to get a task done. The downside of your suggestion is that when we use directives, we need to understand how it works "internally". For example: when you use many directives on the same element, the person who uses the directives need to understand what is the order (that's should be the concern of the person who implements the directives). That's something declarative programming tries to avoid because it's complex for users of the directives – Khanh TO Feb 02 '15 at 13:11
  • @Lu4: The case you pointed is really an edge case. Sometimes, we cannot use purely declarative way to express how we want to render the UI, your case is like an imperative programming case. But we don't have too many edge cases compared to the benefits we get from writing declarative code (simple and easy to use). In these edges, we can still easily avoid problems like I pointed out in the answer. – Khanh TO Feb 02 '15 at 13:13
  • @Lu4: The feature I refer to by `"I believe there is no such feature in standard browser markup"` is standard html tags and html attributes. In browsers, the order of attributes does not matter. Directives are just `extended` html tags and html attributes (`teach` the browser new syntax). – Khanh TO Feb 02 '15 at 13:33
2

To answer your question, there are several possible use cases for Priority. I am not saying they are common, but they are possible and potentially very powerful…

Here are two examples:

Use case A) Wrapping any directive.

By using priority you can get custom code running before or after a given directive. Suppose you have an existing angular application with thousands of ng-click directives, and want to run something before every single ng-click. You can do something like this:

angular.module('app', [])

.directive('ngClick', function($rootScope) {
  return {
    restrict: 'A',
    priority: 100,
    link: function(scope, element, attr) {
      element.bind('click', function(ev) {
        alert("custom click running");
      })
    }
  }
})

.controller('MyCtrl', function($scope) {
  $scope.alert = function() {
    alert('built-in click running!')
  }
})
<!DOCTYPE html>
<html ng-app="app">

<head>
  <script data-require="angular.js@1.0.7" data-semver="1.0.7" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.js"></script>

</head>

<body ng-controller="MyCtrl">
  <a ng-click="alert()">Click me</a>
</body>

</html>

Here your custom click will run before angular build in click.

Use case B) terminating execution

By using priority and terminal: true, you can prevent other directives from executing. For example on all delete actions you can use a custom directive to ask for confirmation. in this case using DOM defined order will be dangerous because you are deleting records.

var app = angular.module('terminal', []);

app.controller('MainCtrl', function($scope) {
  $scope.deleteIt = function() {
    window.alert('Delete called!');
  }
});

app.directive('confirmationNeeded', function() {
  return {
    priority: 10,
    terminal: true,
    link: function(scope, element, attr) {

      var msg = attr.confirmationNeeded || "Are you sure?";
      var clickAction = attr.ngClick;

      element.bind('click', function() {

        if (window.confirm(msg)) {
          scope.$eval(clickAction)
        }
      });
    }
  };
});
<!doctype html>
<html ng-app="terminal">

<head>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.js"></script>
</head>

<body ng-controller="MainCtrl">

  <a href="#" ng-click="deleteIt(id)" confirmation-needed="Are you sure you want to Delete?">Delete with custom message</a>

</body>

</html>
Jose Ch.
  • 3,666
  • 1
  • 17
  • 32