1

This question is related to a previous question which is no longer looked at because it has an upvoted answer: Change jQuery Knob Min/Max value

The following code is a wrapper for the jQuery knob plugin. I am aware of existing angular wrappers, but am using this to further my understanding of the angular framework. Anyway, so my problem is that when the $watch()'s detect changes to the min and max values for the knob, the triggered change event causes some kind of collision within angular, resulting in a $digest already in progress error. Commenting out the two lines within the change function doesn't stop the error, but commenting out the $(elem).trigger('change'); lines does. This breaks my initial hypothesis that the scope.$apply() was the cause. If anyone has any idea what is happening here, any information would be appreciated. Thank you.

App.directive('knobWidget', function () {
    return {
        scope: {
            minbinding: "=minbinding",
            minbindingprop: "@minbindingprop",
            maxbinding: "=maxbinding",
            maxbindingprop: "@maxbindingprop",
            delta: "@delta"
        },
        restrict: 'A',
        require: 'ngModel',
        link: function (scope, elem, attrs, ngModel) {
            ngModel.$render = function () {
                $(elem).val(ngModel.$viewValue)
                $(elem).trigger("change");
            };
            $(elem).knob({
                min: 0,
                max: 1,
                value: ngModel.$viewValue,
                change: function (changeVal) {
                    ngModel.$setViewValue(changeVal);
                    scope.$apply();
                }
            });
            scope.$watch(function () { return scope.maxbinding[scope.maxbindingprop] }, function (newVal) {         
                $(elem).trigger('configure', { 'max': scope.maxbinding[scope.maxbindingprop] + parseInt(scope.delta) });
                $(elem).trigger('change');             
            });
            scope.$watch(function () { return scope.minbinding[scope.minbindingprop] }, function (newVal) {         
                $(elem).trigger('configure', { 'min': scope.minbinding[scope.minbindingprop] + parseInt(scope.delta) });
                $(elem).trigger('change');             
            });
        }
    };
});
Community
  • 1
  • 1
AaronF
  • 2,841
  • 3
  • 22
  • 32
  • I figured out a workaround by wrapping the change trigger in a $timeout, but I'm still interested in understanding the why. I'm certain I'm not the only one having these sort of troubles. Thanks. – AaronF Jun 26 '14 at 15:48
  • 1
    Use $timeout to let the current digest cycle finish before starting another digest, I think this is a common practice with angular programming. – Ye Liu Jun 26 '14 at 15:51
  • Ah, so that's what's going on... Thanks Ye Liu. – AaronF Jun 26 '14 at 15:59

1 Answers1

0

Do not use a $timeout use scope.$$phase instead

if(!scope.$$phase){
   scope.$apply();
}

This will prevent the loop without making your code asynchronous. if you are currently in a digest cycle there is no need to call apply

Erd
  • 11
  • 1
  • 2
  • Note that properties prefixed with `$$` should be considered "private", meaning they are not part of the API and is not intended to be used other than by Angular itself (Relying on them increases the risc that your code breaks when upgrading to a newer version of Angular). – Strille Jun 26 '14 at 20:23