AngularJS $digest by feature: the impossible way

342 views
Skip to first unread message

Xavier Boubert

unread,
Apr 24, 2014, 9:20:59 AM4/24/14
to ang...@googlegroups.com
In big projects, we have many features with many parts of view.
$apply start refreshing the entire page by calling all of the dirty-checking algorythms and then update the DOM elements. Fine, DOM is not modified if it's unnecessary. But calling all of these algos can take lots of time.

Instead of calling $apply everytime, it's possible to use $digest to refresh a specific $scope. Ok so it's possible to call just the algos we wants for specific features instead of the big page.

The first problem:

All of the AngularJS behaviors are based on calling $apply, when the user click buttons, with XHR requests, etc. Unless you want to re-code all of these features, $apply will be called many many times.
In my point of view, the developer should have the control of which features he want to $digest instead of the whole page. It's the basic of a complicated UI with lots of components. Actually it's not possible to have a big project in a single page with many features with AngularJS. This is problematic because AngularJS is designed for this use. Try using Drag&Drop with many components, it's the end of the world.

My simple example:

We have 3 features. We just want to $digest "Feature A" and "Feature C" when user interacts with "Feature A". With a simple ng-click it's not possible because it calls $apply that calls $digest of "Feature B".
With a classic button (jQuery click event) we can $digest "Feature A" then with a Service $digest "Feature C".




The second problem:

One of the main rules of AngularJS is to prevent the start of a second $digest in the first one. For the $apply process it's quite understandable, fine. But for a second independant $digest called by the first, why stop this behavior?

Example:

Once again, we want to refresh just two features ("A" and "C") of the page. But this time, just after the user has changed a value in the view (on "Feature A"). We don't use ng-model because it calls $apply. For the example we're just using jQuery to observe input changes and update the $scope. The focus of the example is within a $watch. After the value has changed, we want to share this value with the "Feature C". Problem, in a $watch we already are in a $digest. AngularJS will break if we are calling a second $digest, so the "Feature C" can't call $digest directly. The only way is to use a setTimeout() to refresh the feature asynchronously, after the $digest ($timeout() calls $apply). With this solution, we can have lots of latencies depending on algos sizes. Try with a D&D for example.


Conclusion

Me and my team are actually creating an IDE webservice. We are using AngularJS since version 1.0.4. and we feel its strength every day. I already have posted an issue in the community found inside our devs (Digest ng-repeat after manual DOM manipulation causes disorganized list).
This is a big project containing a central page with many features (like Solution explorer, toolboxes, etc.). It's not possible to refresh the entire page with $apply everytime. We need to refresh just parts of the UI and it's okay because we are the developers and have the control of these. $apply is not designed for big projects.

So, maybe I'm wrong but I think we really need to change the behavior of AngularJS dirty-checking updates. We need to use $digest most of the time and $apply rarely. For me, ng-click should call the $digest of the current scope and not for the $rootScope, like all of the ng- directives and $http, ectc.

Darlan Alves

unread,
Apr 24, 2014, 6:21:55 PM4/24/14
to ang...@googlegroups.com
You could isolate your main app in different smaller apps, and give them a smaller scope to run a $digest(). I know it would be a huge refactoring, but maybe it's a solution. I'm trying to do the same on some experiments here to see if it works well. I just don't know yet how you could make a channel to communicate between these blocks without using hacky solutions. Maybe a service-level channel via localStorage or something that mimics a socket.

To use resources shared between multiple micro-apps, you could brake apart your app into tiny modules with only the stuff at service level, as if they were just 3rd party libs, then put them into micro-apps' dependencies. These apps would be just a union of smaller modules and some controllers to run the features.

I don't know if it's a nice way to use AngularJS anyway. I'm hoping the guys from Angular team show up to clarify that for us :)

Xavier Boubert

unread,
Apr 25, 2014, 5:07:17 AM4/25/14
to ang...@googlegroups.com
Your solution is pretty tricky.
In that way the problem is still the same, we can't generate app for each sub-features. For example, if we want to digest only one directive in a ng-repeat, it's too heavy to make one app per directive.

Eduardo Cavalcanti

unread,
Apr 25, 2014, 11:12:51 AM4/25/14
to ang...@googlegroups.com

Xavier Boubert

unread,
Apr 25, 2014, 11:24:49 AM4/25/14
to ang...@googlegroups.com
Thanks Eduardo for these links. I wrote a comment on the second post.

Xavier Boubert

unread,
May 4, 2014, 7:45:23 AM5/4/14
to ang...@googlegroups.com
We are talking about the subject with Ben Nadal on his last post: http://www.bennadel.com/blog/2625-triggering-digest-phases-in-related-directives-in-angularjs.htm

I also posted an issue on AngularJS GitHub project in there explaining why the solution with $evalAsync can not works: https://github.com/angular/angular.js/issues/7298

Xavier Boubert

unread,
May 4, 2014, 7:47:09 AM5/4/14
to ang...@googlegroups.com
Ben Nadel sorry.

Reply all
Reply to author
Forward
0 new messages