Ben2307
Ben2307

Reputation: 1022

How to find a watch in scope.$$watchers

I'm using angularjs and need to find the watch of the ng-repeat, because I need ng-repeat to stop working from a specific point. this my code:

<ul>
  <li ng-repeat="item in items">
      <div>{{item.name}}</div>
  </li>
</ul>

I need to find the watcher of the ng-repeat. If I go to scope.$parent.$$watchers[x] and perform splice the watch is removed, but how can I find the specific watch?

Upvotes: 2

Views: 5161

Answers (5)

nachinius
nachinius

Reputation: 340

It's not possible to find the watch (see explanation below), but you can achieve what you wish by use the following directive

app.directive('repeatStatic',
     function factory() {
      var directiveDefinitionObject = {
        restrict : 'A',
        scope : true, // this isolates the scope from the parent
        priority : 1001, // ng-repeat has 1000
        compile : function compile() {
          return {
            pre : function preLink(tElement, tAttrs) {
            },
            post : function postLink(scope, iElement, iAttrs, controller,
                transcludeFn) {
              scope.$apply(); // make the $watcher work at least once
              scope.$$watchers = null; // remove the $watchers
            },
          };
        }
      };

      return directiveDefinitionObject;
     }
    );

and its usage is

<ul>
  <li repeat-static ng-repeat="item in items">
    {{ item }}
  </li>
</ul>

See http://plnkr.co/k9BTSk as a working example.

The rational behind is that the angular directive ng-repeat directive uses internal function $watchCollection to add a self created listener that watchs the items object. Since the listener is a function created during the process, and is not keep anywhere as reference, there is no good way to correctly identify which function to remove from the $$watchers list.

However a new scope can be forced into the ng-repeat by using an attribute directive, in this way the $$watchers added by ng-repeat are isolated from the parent scope. Thus, we obtain full control of the scope.$$watchers. Immediate after the ng-repeat uses the function that fills the value, the $$watchers are safe to be removed. This solution uses hassassin's idea of cleaning the $$watchers list.

Upvotes: 5

hassassin
hassassin

Reputation: 5054

The way that I have dealt with this in the past is that I created a custom directive that copies the logic of the built in ngRepeat directive but never sets up the watches. You can see an example of this here, which was created from the ngRepeat code from version 1.1.5.

The other way, as you mentioned was to remove it from $$watchers of a scope, which is a little stranger since it accesses a private variable.

How this could be done is that you create a custom directive on the repeat to remove the watch that is the repeat. I created a fiddle that does this. It basically just on the last element of the repeat clears the parent watch (which is the one on data)

if (scope.$last) {
    // Parent should only be the ng-repeat parent with the main watch
    scope.$parent.$$watchers = null;
}

This can be modified to fit your specific case.

Hope this helps!

Upvotes: 1

r3m0t
r3m0t

Reputation: 1870

I have a fork of Angular that lets you keep the watch in the $$watchers but skip it most of the time. Unlike writing a custom directive that compiles your HTML it lets you use normal Angular templates on the inside, the difference is that once the inside is fully rendered the watches will not get checked any more.

Don't use it unless you really genuinely need the extra performance because it can have surprising behaviour.

Here it is:

https://github.com/r3m0t/angular.js/tree/digest_limit

Here's the directive you can use with it (new-once):

https://gist.github.com/r3m0t/9271790

If you want the page to never update you can use new-once=1, if you want it to sometimes update you can use new-once=updateCount and then in your controller run $scope.updateCount++; to trigger an update.

More information: https://github.com/Pasvaz/bindonce/issues/42#issuecomment-36354087

Upvotes: 1

Jscti
Jscti

Reputation: 14450

If you don't need angular dual-binding, have you tried a custom directive with a complie function where you construct HTML yourself by creating DOM from scratch without any angular dual-binded mechanisms ?

Upvotes: 0

r3m0t
r3m0t

Reputation: 1870

It sounds like you want to put the bindonce directive on your ng-repeat.

https://github.com/Pasvaz/bindonce

Upvotes: 0

Related Questions