简体   繁体   中英

$digest already in progress - Directive in AngularJS

I'm currently getting the common "$digest already in progress" error message.

Is there a solution where I don't need to use scope.$apply?

Thanks for any help!

directive('ngUsername', ['$http', function($http) {
return {
    restrict: 'A',
    require: 'ngModel',
    link: function(scope, elm, attr, ctrl) {
        //when the scope changes, check the email.
        scope.$watch(attr.ngModel, function(value) {
            $http.get('http://tagsy.co/api/username?token=' + apiToken + '&username=' + value).success(function(data) {
                scope.$apply(function(s) {
                    ctrl.$setValidity('uniqueUsername', data.unique);
                });
            });
        });
    }
}
}]);

<input type="text" name="username" ng-model="username" placeholder="Username" ng-minlength="3" ng-maxlength="15" ng-pattern="/^[A-Za-z0-9]+$/" autocomplete="off" ng-username required/>

<div ng-show="registerForm.username.$dirty && registerForm.username.$invalid">
    <span class="error" ng-show="registerForm.username.$error.required">Please choose a username.</span>
    <span class="error" ng-show="registerForm.username.$error.minlength">Username is too short.</span>
    <span class="error" ng-show="registerForm.username.$error.maxlength">Username is too long.</span>
    <span class="error" ng-show="registerForm.username.$error.pattern">Username can only be letters and numbers.</span>
    <span class="error" ng-show="registerForm.username.$error.uniqueUsername">Username is already taken.</span>
</div>

Before manually starting a cycle you can check if it is in progress with $scope.$$phase . When $scope.$$phase is not set, it is safe to use $apply :

scope.$watch(attr.ngModel, function(value) {
  $http.get('http://tagsy.co/api/username?token=' + apiToken + '&username=' + value).success(function(data) {
    if(!scope.$$phase)
        scope.$apply(function(s) {
                ctrl.$setValidity('uniqueUsername', data.unique);
        });
    else
      ctrl.$setValidity('uniqueUsername', data.unique);
  });
}

You could refactor to avoid code redundancy or create an helper like:

function safeApply($scope, applyFn) {
    if(!$scope.$$phase) $scope.$apply(applyFn)
    else applyFn();
}

scope.$watch(attr.ngModel, function(value) {
  $http.get('http://tagsy.co/api/username?token=' + apiToken + '&username=' + value).success(function(data) {
    safeApply(scope, function() {
      ctrl.$setValidity('uniqueUsername', data.unique);
    });
  });
}

Edit: That is not to say that you should do it in this case, as $http (and most angular services) will take care of that for you.

When you call $http.[verb], the response (success or error) will execute and then a $rootScope.$digest() will get executed. You do not need to do a $scope.$apply inside this method.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM