Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why ngIf has higher priority than {{ }} (interpolate)?

Tags:

angularjs

What is the reasoning behind setting ngIf priority (600) higher than {{ }}(100)? Shouldn't it have a lower priority to allow {{ }} inside ng-if attribute value?

I would like to have a condition inside a $scope variable:

Controller:

app.controller('MainCtrl', function($scope, $http, $parse) {
  $scope.hide = "check === 'hidden'";
  $scope.parsecond = function (cond) {
    return $parse(cond)($scope);
  };

});

Template:

  <body ng-controller="MainCtrl">
    <div ng-if="!{{hide}}">funky ng-if div</div>
    <div ng-hide="{{hide}}">ng-hide div</div>
    <div ng-if="!parsecond(hide)">ng-if div</div>
    <input type="input" ng-model="check" />
  </body>

ng-hide works fine since it parses the contents of the hide variable and returns "check === 'hidden'" which then gets evaluated by ng-hide directive.

But ng-if tries to evaluate {{hide}} before interpolate has had a chance to parse the string hence ng-if throws an exception.

The only solution I've found is call a function that basically does the job of the interpolate directive and returns the parsed content.

Plnkr showing the issue: link

EDIT:

After reading documentation I've found better way of doing it without the need of a custom method on the $scope since angularjs has already a method that parses a variable against its current $scope ($eval).

So my solution would be:

  <body ng-controller="MainCtrl">
    <div ng-if="!$eval(hide)">funky ng-if div</div>
    <div ng-hide="{{hide}}">ng-hide div</div>
    <div ng-if="!parsecond(hide)">ng-if div</div>
    <input type="input" ng-model="check" />
  </body>

Updated plnkr: link

Although this still doesn't explain why ngIf has higher priority.

EDIT 2:

Just so people understand that it's not the same:

For example:

Controller:

$scope.value = "hi";
$scope.condition = "value === 'bye'";

HTML:

<div ng-hide="condition"></div> <!--This will be evaluated to true since !!"value ==='bye'" = true. -->
<div ng-hide="{{condition}}"></div> <!--This will be evaluated to false since value !== 'bye' = false -->
<div ng-if="condition"></div> <!--This will be evaluated to true since !!"value === 'bye'" = true. -->
<div ng-if="{{condition}}"></div> <!--This will give an exception since ngIf directive ran before interpolation directive in the $compile step. -->
<div ng-if="$eval(condition)"></div> <!--This will be evaluated to false since value !== 'bye' = false. -->

My conclusion is that it safer to use $parse if you want the directive to evaluate/set a watch in the string rather than in the property on the scope. Although it's true that I could use {{ }} for ng-hide/ng-show or any directive that has a lower priority than 100 but I'm guessing it's not safe since I'm depending in the compiling order and it's not 100% clear that it won't change in future patches.

like image 261
Wawy Avatar asked Apr 24 '14 10:04

Wawy


Video Answer


1 Answers

ng-if expects its value to be an angular expression - under the hood it just makes use of $scope.$watch. Therefore, if you want to condition including content of ng-if on some variable defined on the scope (let say: scope.hide), you put ng-if="hide" in your mark-up. No double curly braces here.

Now back to your question: it is true that ng-if has priority of 600, but $interpolate is angular's service - not a directive. As such $interpolate does not define priority. Where did you get 100 from?

UPDATE

You can always condition including content of ng-if on some function (let say scope.conditionFn) by putting in your mark-up: ng-if="conditionFn()".

UPDATE 2

I updated your PLNKR to make it working. The inconsistencies between ng-if and ng-hide in your plunker had nothing to do with priority of interpolation taking place in $compile.

UPDATE 3

It seems that you are right that order of interpolation plays a role here, but... I really do not see any good reason to interpolate inside of angular's expression. The reason why ng-if has relatively high priority is that it removes/adds transcluded content from/to DOM, whereas ng-hide just shows/hides the transcluded content. I think it is a pure coincidence that one directive seems to work and the other not. But if you do not use unnecessary, superfluous tricks, both do work as intended, what my plunker shows.

like image 50
artur grzesiak Avatar answered Oct 14 '22 01:10

artur grzesiak