Jakobovski
Jakobovski

Reputation: 3390

Angular ngIf unexpected behavior

I have simple HTML form named myForm. One of the form fields has ng-if="true"on it.

console.log(myForm) shows that the form is an array containing 5 items. console.log(myForm.length) returns the number 4.

Please see the example http://plnkr.co/edit/MHi6FpEs9l0wbC96Cx6b?p=preview

Upvotes: 1

Views: 724

Answers (1)

user1234
user1234

Reputation: 8978

This hung me up recently as well, and after looking into it a little I believe the issue is that ng-if sets a $watch in its link function, so even though your directive's link function may execute after ng-if, the $watch won't be triggered until the next $digest (or later in the same one? Not entirely sure).

So both directives are compiled, then linked, and finally $watch is triggered and the ng-if DOM element is inserted.

If you wrap your code in a $timeout it will reflect the proper value, as the $digest will have completed:

http://plnkr.co/edit/T8dXfRhsgFWGg0I232Vo?p=preview

The reason that the console shows the length of the form as 4 is because when your directive link function runs, ng-if has been compiled which removes your input from the DOM and replaces it with a comment that looks like <!-- ngIf: true -->, but the $watch declared in the ng-if link function has not run yet.

ng-if is waiting on the $watch to then add a clone of your input back to the DOM if the $watch expression is truthy, but since it hasn't run at the time of your link function and a comment is not a valid form input, the form length is 4.

Order of events if I understand it correctly:

  1. ng-if compilation (remove the input and replace it with a comment, form length 4)
  2. your directive compilation
  3. ng-if linking
  4. your directive linking (still no input, form length is still 4) <- here is where you are setting scope.formLength
  5. ng-if $watch (input clone added if expression is true, form length 5)


Form.length returns 4 despite there still being 5 elements because as per the spec, form.length returns the length of the elements array, which includes button, fieldset, input, keygen, object, output, select, and textarea but not div :)


The reason console.log shows a length of 4, but 5 valid inputs is because you are passing in a reference to the form element. At least in Chrome, an instant snapshot of the object passed in to console.log is displayed in italics, but when you click to expand the object in the console the current object that is referenced is shown, which is why the form is shown to have 5 valid inputs. Unfortunately the snapshot in italics is condensed, so you can't see from the console.log that the form only has 4 inputs.

If you clone the input at the time of the console.log you can see that it actually doesn't have the 5th input.

var myFormClone = myForm.cloneNode(true);
console.log(myForm) //has 5 inputs by the time you see it
console.log(myFormClone) //has 4 inputs

Relevant chromium issue: https://code.google.com/p/chromium/issues/detail?id=50316

Upvotes: 3

Related Questions