|AngularJS $digest by feature: the impossible way||Xavier Boubert||4/24/14 6:20 AM|
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?
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.
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.
|Re: AngularJS $digest by feature: the impossible way||Darlan Alves||4/24/14 3:21 PM|
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 :)
|Re: AngularJS $digest by feature: the impossible way||Xavier Boubert||4/25/14 2:07 AM|
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.
|Re: AngularJS $digest by feature: the impossible way||Eduardo Cavalcanti||4/25/14 8:12 AM|
|Re: AngularJS $digest by feature: the impossible way||Xavier Boubert||4/25/14 8:24 AM|
|Re: AngularJS $digest by feature: the impossible way||Xavier Boubert||5/4/14 4:45 AM|
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
|Re: AngularJS $digest by feature: the impossible way||Xavier Boubert||5/4/14 4:47 AM|
Ben Nadel sorry.