I asked this question on programmers stackexchange a few days ago and didn't receive any replies, so I thought I'd try here.
I have a table that has around 60 rows and 20 columns. Each cell has a directive with an isolated scope. I use 2 nested ng-repeats to generate the table (one for rows and one for columns), but I'm wondering how angular handles scope when a digest cycle causes a re-generation of the directives in the ng-repeats?
Does it destroy all the previous isolated scopes in my directives and create new ones, or are those old isolated scopes now orphaned somewhere and memory leaking as more and more digests take place?
Is this something that I, as a developer, should be aware of and make sure to handle the destruction of any isolated scopes?
The ng-repeat creates a $$hashKey attribute in your $scope for all the values you are displaying. Whenever the $digest is called, it checks if the values changed or not. ng-repeat is usually one of the culprits in slow angular applications for multiple reasons.
You may not need to watch the variables. If you just want to show some data which doesn't get updated often you might as well remove the double-binding so angular doesn't have to create entries for them in the $scope
Never use a function result as a ng-repeat value because the function will run on every $digest cycle
Don't use filters since they get called twice in the $digest cycle and slow down the ng-repeat
Here's some more information about ng-repeat and angular performance.
Related
I am trying to improve performance of my angularjs project.I am also read that ,for better performance number of watchers on a page should be limited to 2000.
So my questions is,
Actually do watchers really watching for all variables defined to scope(like $scope.name variable ) OR just for scope variables bound to view?
Thanks for all the answers...
actually watchers DONT LOOKUPS on scope variables. when angular $digest cycle runs( in case of server response or button clicked or ...) , all of watchers runs,and check if last value of that watched variables changes or not, based on this condition watch handlers may be run or not.
so the numbers of $scope variables does not matters.
* with a correct design you can stop using $watchs.
I have a situation where I am generating a LOT of rows and columns using Angular and the performance is terrible!!
I'm assuming that it's because my ng-repeat is creating a new scope for each of the elements that it creates, however, this particular function doesn't need to watch anything. I'm simply using ng-repeat as a loop to generate HTML that will never change.
Is there another angular method that I can use to loop through model data and generate HTML without using ng-repeat that will improve performance?
You can create a directive which will accept the ng-repeat values and then do whatever you want to do in that directive e.g. create a table which will have those values.
You actually need the scope to access the data for the current iterations item.
However, as your data never changes, you might get take a look at bindonce or the slyRepeat directive.
I have a page that has a number of directives. There are a number of directives in the header/navigation each with there own scope. There is also a ng-repeat of 25 items and each one of those creates a directive each with its own scope.
One of the directives includes a form that includes a custom filter to display form errors, it looks like this:
<span>{{ createProjectForm.name.$error | nagParseErrors }}</span>
Now the concern I have right now is that nagParseErrors is being executed about 33 times when anything in any scope changes even though this data createProjectForm.name is binded to (with ng-model) is only contained in the controller scope and the directive's scope containing the form (which is just being passed to the directive from the controller scope). I know it is related to the number of scopes (or directives) on the page because if I limit the ng-repeat from 25 items to 1, the filter is only called 9 times. This also happend for built-in filters (like json, and it even runs more times).
Is there something I might be doing wrong here or is this in fact how it should work in AngularJS?
BTW, I realize now that displaying the errors might be better off as a directive than a filter I am planning on going the directive route however I would like to clear up my understanding of filters here since I will probably run into this at some point down the road.
This has been addressed numerous times. All AngularJS expressions will be constantly re-evaluated throughout the lifecycle of the app. This is how two-way databinding in AngularJS works.
So, there's nothing wrong with your code. It's just that you need to make sure your filter is idempotent (returns the same output given the same input).
For more info take a look at Why Scope.$apply() calls $rootScope.$digest() rather than this.$digest()? and scope docs.
I have an angular scope containing two things:
a giant table with 10k rows that takes a second to render
some small extra information on top of it in a fixed overlay header bar
Depending on how far you have scrolled down the page/table, I have to update one of the small info bits in the header; you can think of it as a % counter how far you've scrolled.
To get the current scroll position, I have written a directive (you can also find it here):
app.directive "setWindowScroll", ->
restrict: "E"
scope:
setVariable: "="
link: (scope) ->
$(window).scroll ->
scope.$apply ->
scope.setVariable = $(window).scrollTop()
My problem is that this makes scrolling around in the table u*nsuably slow*. This is because the veriable I write to with my directive is in the extra-info in the scope, and changing that seems to cause angular to dirty-check the whole table for changes when my $apply is called.
I know that this scrolling will never change anything in my table, and would like to restrict my $apply to affect the header part of my site.
How can I make angular not dirty check the table?
Angular does the dirty checking under a process called digest. When you do a call to $scope.$digest() it will propagate to every child scope of $scope. To notify angular about changes you usually do a $scope.$apply(). At the end of the $apply() angular does a digest on the root scope, which triggers a diggest on every scope in your application. So to avoid the digest on in your scope with the big table scope, you should make sure that it is not the child of the extra information scope, and rather than doing an $scope.$apply() in your directive you could do a $scope.$digest(). This might be a bit confusing so I've made a plunker that tries to show the difference. Open your console and see the difference in the scroll event and button click:
http://plnkr.co/edit/45gKhAIt0DrozS7f0Bz2?p=preview
// this will only cause a digest in the current scope and its children
angular.element($window).bind('scroll',function(){
$scope.scrollY = $window.scrollY;
$scope.$digest();
})
// this will cause a digest in every scope
angular.element($window).bind('scroll',function(){
$scope.scrollY = $window.scrollY;
$scope.$apply();
})
When all this is said, it's a very unusual thing to do - and probably not a good idea for a number of reasons (angular doesnt scale well with thousands of elements, you can't use any of the angular event directives (ngClick etc) because they're all wrapped in $apply) - but if you can't render the table on the server side you can give this a try.
I would like to know if this is possible to do too, but -- regardless -- you might want to consider not drawing the entire table all at once.
Instead limit it (and the amount of associated data/controllers in Angular-world) to just the rows that are visible based on current scroll position + some cached rows above and below. You can still keep all the data in an array on the client, but only expose a small subset to Angular at any time.
One way to do it might be to have a small array in Angular-world which you render using ng-repeat, and then you add and remove elements in this small angular array from the big non-angular array based on scroll position.
This way Angular only knows about a small subset of your data, and should render much faster. I think this should work quite well in general because it's also much less work for the browser which doesn't need to maintain a DOM tree and rendering for 10k rows.
I'm having some AngularJS trouble with firing off a directive when ng-repeat has completed an update. I have three named arrays, switched to via three links, allowing the selected array to be displayed by the ng-repeat. I'd like to fire some code off when it's finished as I'm planning to set some element attributes for D3 to use.
I've tried checking scope.$last in my directive, but this isn't called at the end of every ng-repeat process. If some of the data remains the same, it may not set scope.$last to true.
http://plnkr.co/edit/hwmOlI6YrgS4H1C1h7k2?p=preview
So, what's the best way to trigger code in a directive when ng-repeat has finished?
Here is a solution for you. Note that the last triggers each time now.
http://plnkr.co/edit/xV1quqzorzxliS2shrP4?p=preview
You just need to $watch the $last variable and it will work fine. This helps in situations where the scope is not created but just updated with new values. Your directive gets created once and if one of the repeated variable just changes values ng-repeat optimizes and just updates values ( as opposed to removing all the values and re-creating the new ones. ). In this scenario, the $scope.$last will be an updated variable and not something that gets "created". So, you will need to $watch it.