How to "deduce" an expression

Say I have ng-repeat with a large array.

When ng-repeat starts, it adds every element of this array to an isolated area, and also has the array itself in the area. This means that $ digest checks the entire array for changes, and in addition, it checks every single element in this array for changes.

See this plunker for an example of what I'm talking about.

In my use case, I never change a single element of my array, so I don't need to look at them. I will only ever modify the entire array, in which case ng-repeat will re-render the entire table. (If I'm wrong, let me know.)

In an array of (say) 1000 rows, this is another 1000 expressions that I do not need.

How can I unregister each item from an observer while viewing the main array?

Perhaps instead of unregistering, I could have more control over my $ digest and somehow skip every single line?

This particular case is actually an example of a more general problem. I know that $ watch returns the "deregisteration" function , but this does not help when the directive registers the clock, which in most cases.

+66
angularjs
Nov 30 '12 at 19:12
source share
5 answers

To have a repeater with a large array that you do not look at every element.

You need to create a custom directive that takes one argument and an expression for your array, and then in the binding function that you just watch this array, and you will have a binding function that updates the HTML programmatically (rather than using ng-repeat)

something like (psuedo-code):

app.directive('leanRepeat', function() { return { restrict: 'E', scope: { 'data' : '=' }, link: function(scope, elem, attr) { scope.$watch('data', function(value) { elem.empty(); //assuming jquery here. angular.forEach(scope.data, function(d) { //write it however you're going to write it out here. elem.append('<div>' + d + '</div>'); }); }); } }; }); 

... which seems like a pain in the butt.

Alternative hacker method

You may be able to go through $scope.$$watchers and examine $scope.$$watchers[0].exp.exp to see if it matches the expression you want to delete, then delete it with a simple splice() call splice() . PITA here, is that tags, such as Blah {{whatever}} Blah between tags, will be an expression and will even include carriage returns.

At the top, you can just skip the $ n space of your ng-repeat and just delete everything and then explicitly add the clock you want ... I don't know.

In any case, this seems to be a hack.

To remove an observer from $ scope. $ watch

You can unregister $watch with the function returned by calling $watch :

For example, in order to fire only $watch fire once:

 var unregister = $scope.$watch('whatever', function(){ alert('once!'); unregister(); }); 

You can, of course, call the unregistration function at any time ... it was just an example.

Conclusion: There really is no great way to do what you ask.

But one thing you might think: is it even worth the worry? Also, is it really a good idea to have thousands of records loaded into dozens of DOMElements each? Food for thought.

I hope this helps.




EDIT 2 (bad idea removed)

+86
Nov 30 '12 at 19:53
source share

$ watch returns a function that discards $ watch when called. So this is all you need for watchOnce:

 var unwatchValue = scope.$watch('value', function(newValue, oldValue) { // Do your thing unwatchValue(); }); 
+12
Feb 22 '16 at 18:07
source share

Edit: see another answer that I posted.

I went and implemented the idea of ​​Brilliant in a great way. My ngOnce directive simply destroys the region of the child that ngRepeat creates for each element. This means that scope is not reached from parents scope.$digest , and observers are never executed.

JSFiddle source and example

The directive itself:

 angular.module('transclude', []) .directive('ngOnce', ['$timeout', function($timeout){ return { restrict: 'EA', priority: 500, transclude: true, template: '<div ng-transclude></div>', compile: function (tElement, tAttrs, transclude) { return function postLink(scope, iElement, iAttrs, controller) { $timeout(scope.$destroy.bind(scope), 0); } } }; }]); 

Using it:

  <li ng-repeat="item in contents" ng-once> {{item.title}}: {{item.text}} </li> 

Note ng-once does not create its own scope, which means that it can influence elements of kinship. They all do the same thing:

  <li ng-repeat="item in contents" ng-once> {{item.title}}: {{item.text}} </li> <li ng-repeat="item in contents"> <ng-once> {{item.title}}: {{item.text}} </ng-once> </li> <li ng-repeat="item in contents"> {{item.title}}: {{item.text}} <ng-once></ng-once> </li> 

Note this might be a bad idea

+8
Jun 28 '13 at 12:16
source share

You can add the bindonce directive to your ng-repeat . You need to download it from https://github.com/pasvaz/bindonce .

edit : a few caveats:

If you use interpolation {{}} in your template, you need to replace it with <span bo-text> .

If you use the ng- directives, you need to replace them with the correct bo- directives.

Also, if you put bindonce and ng-repeat on the same element, try either moving bindonce to the parent element (see https://github.com/Pasvaz/bindonce/issues/25#issuecomment-25457970 ) or add track by to ng-repeat .

+3
Nov 25 '13 at 14:20
source share

If you are using angularjs 1.3 or higher, you can use single binding syntax like

 <li ng-repeat="item in ::contents">{{item}}</li> 

This will bind the value and remove observers after starting the first digest cycle, and the value will change from undefined to defined for the first time.

A very useful BLOG on this one.

+1
Jan 31 '16 at 8:29
source share



All Articles