Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

perf(Scope): $postDigestWatch #5828

Open
kseamon opened this issue Jan 15, 2014 · 9 comments
Open

perf(Scope): $postDigestWatch #5828

kseamon opened this issue Jan 15, 2014 · 9 comments

Comments

@kseamon
Copy link
Contributor

kseamon commented Jan 15, 2014

Currently, directives that do nothing more than watch an expression and update the dom in response use $watch (for the most part).

This subjects them to repeated dirty checks every $digest cycle and might even trigger multiple dom updates.

Since they will never update the scope themselves, there should be another kind of $watch that will execute exactly once after the scope has stabilized in a $digest.

@bfricka
Copy link

bfricka commented Jan 21, 2014

👍 Totally agree on this one. We had some issues integrating a custom carousel implementation due to conflicts w/ ngAnimate. We wanted to allow arbitrary transclude content to use ngAnimate-supported directives (ngShow, ngClass, etc), while still being able to manage transitions of certain sub-elements within the carousel directive.

Hard to describe, but basically it was to allow for a generic parallax-like sub-directive that works based on the ratio of how "active" a particular slide is (we should be open sourcing it soon).

The problem is the way ngAnimate hooks into the private Scope.$$postDigest() function and uses $timeout. Being able to hook into $$postDigest would have worked, but obviously we didn't choose that route since it's a private method. Instead we ended up having to create nested timeouts to guarantee execution order (e.g. $timeout(function{ $timeout(someFn); }, 10, false);)

@g00fy-
Copy link

g00fy- commented Jan 22, 2014

@kseamon what about $$postDigest see: http://youtu.be/zyYpHIOrk_Y?t=22m20s

@bfricka
Copy link

bfricka commented Jan 22, 2014

I just addressed this in my last comment. $$ indicates "private", and we don't advocate the use of a private Angular method. Why it's private, I don't know (I had no issues using it as a POC)

@kseamon
Copy link
Contributor Author

kseamon commented Jan 22, 2014

Aside from the privacy issue, $$postDigest registers a one-time callback for the current $digest cycle.

In order to do what I'm talking about you would need to do something like this:

var hasRegistered = false, oldValue;
$scope.$watch(function() {
  if (hasRegistered) return;
  hasRegistered = true;
  $scope.$$postDigest(function() {
    hasRegistered = false;

    var value = myExpressionToWatch($scope);
    if (value !== oldValue) {
      oldValue = value;
      // Finally, do something with value
    }
  });
});

With $postDigestWatch, it would just be:

$scope.$postDigestWatch(myExpressionToWatch, function(value) {
  // Do something with value
});

@bfricka
Copy link

bfricka commented Jan 23, 2014

This is really a smart idea. I see what you mean now.

@DavidSouther
Copy link
Contributor

+1 - just ran into a similar situation, and we use the $timeout workaround way too much.

@DavidSouther
Copy link
Contributor

Though, for edification, how would this differ from $evalAsync?

@timruffles
Copy link
Contributor

@DavidSouther $evalAsync forces a digest.

@petebacondarwin
Copy link
Contributor

Interestingly this is kind of what happens now for $onChanges hooks. We implemented an onChangesQueue that allows directives to schedule calls to $onChanges. This is then flushed in the $$postDigest, causing a maximum of one call to each controller's $onChanges hook per digest.

Perhaps we could think about abstracting this out of the $compile service?

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

7 participants