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.
Related
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.
I am using Angular Meteor and I have a controller that subscribes to a publish-composite publish which returns two cursors. What I am trying to do is to ng-repeat on one cursor and get value from the second cursor based on the value of the first one. Like this:
{{getName(a.id)}}
where a is one object of the first cursor, and in the controller I have $scope.getName(id) function that returns name from the second cursor, like this:
name = second.find({ID: id}).fetch()[0].name
it worked but the problem is the getName function gets hit for unnecessary number of times. Is there a better way to get data from the second cursor based on ng-repeat object of the first cursor? Is nested ng-repeat the way to do? How can I do it?
Thanks.
It seems as if you are focused on the performance of the ng-repeat. Glad you feel this way! From what I remember, the ng-repeat has been a focus of optimization across many 3rd party libraries and discussions.
In your case, my first attempt would be to bind the value inside the ng-repeat only once. You can do this using the :: syntax on the expression.
For example, this would call the expression once.
<ul ng-repeat="a in items">
<li>{{::getName(a.id)}}</li>
</ul>
At the end of your question you mentioned a nested repeat but your question only mentions a single use of it. Am I missing something?
Disclaimer: I've never used Angular Meteor (or even standalone Meteor) so there could very well be a better way do solve your problem.
via: https://docs.angularjs.org/guide/expression
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>
I am new to angularjs and trying to learn concept of filters.But in my case angularjs is calling the filter twice.I was expecting filter to run only once but its running twice. Cant understand why this is happening..
here is my plunker...
If i don't take your question wrong, this is the same question as here
Angularjs uses a 'dirty-check' approach, so it need to call all the filters to see if exists any change. After this it detect that have a change on one variable(the one that you typed) and then it execute all filters again to detect if has other changes.
To be more clear understanding, stackoverflow this question will explain in more detail by expert in angularjs
Here is another plunker same calling filter twice
The first call is from the watchers that are detecting the change. Because there is one then they need to be called again to see if is there news changes because a watcher can make changes.
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.