crisbeto / angular-svg-round-progressbar

Angular module that uses SVG to create a circular progressbar
https://crisbeto.github.io/angular-svg-round-progressbar/
MIT License
741 stars 174 forks source link

Minimize watcher count #83

Closed Ivshti closed 8 years ago

Ivshti commented 8 years ago

I've been trying to optimize an angular (1) application, and I found out that every instance of round-progress is additional watchers for max, color, bgcolor, radius, stroke and responsive. Is it possible to use one-time binding for those?

Ivshti commented 8 years ago

Actually, even the properties that don't get set are watched

crisbeto commented 8 years ago

You can set a one-time binding in your own templates, just prefix the data binding with ::. Otherwise excluding properties that aren't set from the $watchGroup makes sense.

Ivshti commented 8 years ago

afaik the watch group includes a fixed list of properties - how about filtering to only the ones which are set by the user?

also, I tried doing things like: max="::100", didn't work

as for stuff like the color, which are strings - I have no idea; just putting a "::" does not make sense, doing a ::"#ee...." does make sense but the binding doesn't work that way

crisbeto commented 8 years ago

By using one-time bindings, the watchGroup will still watch for changes, it just won't fire as often. I'll add filtering only for the specified properties when I get some more time.

Ivshti commented 8 years ago

I understand that, but can you give specific examples of how one-time binding works with round-progressbar?

I tried: max="::100" radius="::2.5" for starters, did not work at all (as if I didn't passed the props)

Angular 1.5.7

crisbeto commented 8 years ago

max="100" radius="2.5" Won't fire watchers at all since it doesn't have a data binding. The cases where it would are something like max="{{ max }}" in which case you can do max="{{ ::max }}" to turn it into a one-time binding.

Ivshti commented 8 years ago

Got it. I am confused by the fact that the watchers exist, I don't know when they're fired. Let me illustrate: screen shot 2016-07-06 at 19 35 37

crisbeto commented 8 years ago

Yes, the way it works is that it passes an array of all the properties to $watchGroup, which then calls $watch on each of them behind the scenes. The difference between doing this and individual watches is that they all fire the same callback when any one of them changes.

Ivshti commented 8 years ago

Since this is a relatively vital issue for us - this directive adds around ~1000 watchers to our application - I implemented a (ugly) solution

            if (scope.noListen) { 
                Object.keys(options).forEach(function(k) {
                    if (scope[k]) options[k] = scope[k]
                });
                renderState(scope.current, scope.current, true);
                renderCircle();
                return;
            }

after defining renderState (and ofc adding "noListen: '@'")

Please think of a more elegant way to do this, and I'd PR

crisbeto commented 8 years ago

Done in 0.4.6. Note that if you specified something like max="100", it will still be watched, even though it doesn't have a data binding.