How often does code in an *ngIf condition in Angular 2+ fire? - javascript

I am curious about how *ngif works:
<img *ngif="isMediaMessage(message) === 'audio'" src="assets/img/audio1" />
1)
When I put a console inside the isMediaMessage function, the console prints out indefinitely; I wonder why it does that. Is it because of the digest loop? dirty checking? I am reading up more on these.
2) Should I use less data binding if I want to reduce rendering time?
3) Would you guys say this article is up to date?
This might be related.

This is concerning to the digest loop/detection cycle and the watches on the page.
Every time there is a change in the page and the queue of dirty checking is running then the mechanism of detection is running will reevaluate the ngIf and your code/condition of ngIf will fire.

Related

AngularJS recalculate "nested" functions on data change

There is possibly a solution out there for this but i can't seem to find it or if i found it already i can't understand it.
I have a AngularJS app and there is the following setup:
in the view:
<button ng-click="data=data+1"></button>
<div>{{getValue("something")}}</div>
<div>{{getAnotherValue("anotherThing")}}</div>
In the controller:
$scope.getValue=function(param){
return param+$scope.otherValues+$scope.data;
}
$scope.getAnotherValue=function(param){
return param+$scope.evenOtherValues+$scope.getValue("someOtherParam");
}
When i click the button, the "getValue()" function is recalculated automatically but not the "getAnotherValue()". Is there a way to recalculate everything what is effected by the data change, even in this nested (or even more nested) situation?
Are u sure getAnotherValue() is not updating? I set up a fiddle myself and it DOES updates.
In fact angular will ensure that the data bind to UI(DOM) to be correctly presented by adding $watch (watcher keeping track of the expression) to them.
Whenever there is something changed, there will be a digest cycle in which angular will go through all the watchers and check whether the data watched has changed (by recalculating it if it is a function).
For any watcher if the value has changed, angular will repeat the check process again, until all watchers reporting there is no change in the value they are watching.
If we write the process in steps:
1.start a digest cycle
2.ask the watchers one by one if there is any change in value
3.if any watcher reports there is a change, go back to step 1
4.if no change is reported, finish digest cycle and update UI.
However there could be exception that there are too complicated and nested watchers or any watcher loops, which will cause angular to reach the maximum number of digest cycles (default is 10). Angular will break the digest process and an exception will be thrown under this circumstance.

Buttons 'flickering' in angular application using ng-if or ng-show

I've had this same issue in two different angular applications I've worked on and yet I have been unable to find any discussion of this problem - which makes me think perhaps I am missing something. Let's say I have a view of a 'task' which can be in a number of different states including 'pending', 'accepted' and 'completed'. Different action buttons will be shown depending on the state of the task, for example:
<button ng-if="task.status === 'pending'" ng-click="ctrl.acceptTask()">Accept</button>
<button ng-if="task.status !== 'accepted'" ng-click="ctrl.acceptTask()">Flag</button>
<button ng-if="task.status === 'accepted'" ng-click="ctrl.flagTask()">Complete</button>
The issue is that when the user clicks the accept button, for a brief period both of the buttons below will be displayed. It's as if angular is sequentially working through the DOM and for the brief period between ng-ifs, both the 'flag' and 'complete' button are displayed because only one has been updated. This occurs for ng-show as well.
Note, this is not an issue that can be solved with ng-cloak, which is only there to prevent a template being displayed before angular has done its magic.
Given I've encountered this issue on both of the two large angular applications I've worked on, it must be a common problem. Any suggestions as to how this is typically solved? (PS, the above HTML is just an example of what I mean, it's not my actual template.)
That is because ngIf completely removes and recreates the element in the DOM. When an element is removed using ngIf its scope is destroyed and a new scope is created when the element is restored. The scope created within ngIf inherits from its parent scope using prototypal inheritance. also,ngIf recreates elements using their compiled state.
Indeed having ngIf evaluate to false will remove the element. But only if it is not set to true immediately after, as angular only refresh the DOM (your HTML) when it has the opportunity to do so, i.e. after the current synchronous block of code.
Below code snippet does not have any effect:
$scope.task.status= 'pending'; <br/>
$scope.task.status= 'accept';
This will
$scope.task.status= 'pending';
// $timeout will wait for a digest cycle before executing the code
// Angular will remove the button from the DOM
$timeout(function() {
$scope.task.status= 'accept';
// Here a second digest cycle happen, angular will put the button back into the DOM
});
Please note that it might be due to your angular app emerges many watchers due to heavy DOM contents. I have made one simple plunker to replicate your scenario but not facing it. You can check and reduce watchers by adding available chrome plugin and/or many others way like this. Just google it and you can find a treasure of lots of fruitful information to improve watcher count of your app.
Ok now I got it what exactly you do require here:) See !! This is what angular docs told us "Note especially the powerful ng-switch that should be used instead of several mutually exclusive ng-shows." One of the good read on "When to use what!!!". So i hope flickering buttons will not happen if you use the ng-switch at those instances or alternatively we can achieve the scaffold of solution that is quite generic and works without worrying about current scope at all by using directive initialization function to inter-instances communication. See this in which i have applied class to multiple buttons in mutually exclusive way.
The below ng-if statements ensures that it will display one at a time. If you see it both at the same time it should be because something in your code is messing with angular digest cycle which delaying the two way binding of the fields and rendering of the buttons.
<button ng-if="task.status !== 'accepted'" ng-click="ctrl.acceptTask()">Flag</button>
<button ng-if="task.status === 'accepted'" ng-click="ctrl.flagTask()">Complete</button>

ng-repeat loop more than the number of elements

I am trying to implement ng-repeat with ng-style. It works perfectly. However, I do not understand why my method is called more than the number of elements in my ng-repeat!
Do you know why?
To complete my explanation, I created a : JSFiddle
I think that when your html code is compiled, it executes ng-style directive even if there is no data (when items == null). After that your controller changes $scope.items, forcing other calls to $scope.getStyle().
I think if you put a ng-if="items != null" within ng-repeat, your function will be called only 5 times.
Solution looks fine. The reason for multiple calls is angular digest loop. You can read about it here: http://www.benlesh.com/2013/08/angularjs-watch-digest-and-apply-oh-my.html
It works by running the loop and looking if the values changed for the watches. When they stabilize it ends. You can have multiple passes of the event loop in angular app and that is pretty normal. The limit is set afaik for 10 iterations. If bindings do not stabilize then exception is thrown.
Additional reading, highly recommended:
http://teropa.info/blog/2013/11/03/make-your-own-angular-part-1-scopes-and-digest.html
Part Keep Digesting While Dirty is the answer to your question i believe.
So it is by design.

Call a javascript function when ng-repeat finishes

This is my first attempt at Angular so please bear with me.
I have a jQuery slider that usually is initialized at document ready. However this doesn't work when the images are being populated by angular since it will not have finished rendering the DOM.
Is there a way to detect when ng-repeat finished rendering the DOM so that I can call my function?
Thanks in advance
Check out this answer Stack Overflow. Setting a timeout is never a good way to wait til something finishes. I would suggest a custom directive checking to see if you are scope.$last, then you can broadcast an event to the controller.

"10 $digest() iterations reached. Aborting!" due to filter using angularjs

Take a look at the following:
https://dl.dropbox.com/u/4571/musicopeTypescript/musicopeTypescript/index.html
when you type "a" in the input box, you obtain the 10 $digest() iterations reached. Aborting! error.
Do you have any idea, why that happens?
EDIT: Here is the code that makes problems:
http://embed.plnkr.co/PTkvPc
EDIT: It looks like it is a problem of Song.clone. If I replace it by angular.copy, then it works. Anybody could explain that?
Here is the working version:
http://plnkr.co/edit/8Jk1pR?p=preview
Is your filter modifying the original data somehow? That's the only specific thing that looks like it would cause the infinite digest cycle.
EDIT: in regards to different cloning functions lead to different behavior.
I suspect one is doing deep cloning, the other is not, and in one case AngularJS is checking object equality and your filter is creating new objects each time, causing the problem.
I'd suggest breaking up some of that logic and perhaps moving some of it into the controller or additional filters. The filter that narrows down your array should only be doing that, and just return references to the original objects. Then you can write other filters to manipulate the tags, etc.
Also +1 for Abba. :P
To understand why is this is happening, it's good to understand how angular works runtime. Basically there are watchers that keep returning back different values, and so it keeps going through the $digest loop and then stops it from infinitely looping. From their $digest() documentation:
Process all of the watchers of the current scope and its children. Because a watcher's listener can change the model, the $digest() keeps calling the watchers until no more listeners are firing. This means that it is possible to get into an infinite loop. This function will throw 'Maximum iteration limit exceeded.' if the number of iterations exceeds 10.
Without knowing what your code is doing, it's hard to give a specific solution to why this is happening in your case, but this should answer your questions as to when this error is thrown.

Categories