1

I have a registration form validating client-side that works fine. My app has also a server-side validation which returns the client a JSON with errors for each field.

I try to handle them like this:

// errors = { 
//    "username": [
//        "Account 'test2@test.com' already exist!"
//    ]
// };

for(var field in errors) {
  $scope.RegForm.$setValidity(field, false);
  $scope.RegForm[field].$error.server = errors[field].join('\n');
}

The problem is that errors remain visible even when I change field. I need to set validity back to true, and remove server error at some moment. Just not sure how and when.

How to properly treat server data in order to make them change? $asyncValidators will not work, because in the case of username field I'm not allowed to register user, just to see if such username is free.

Bunyk
  • 7,635
  • 8
  • 47
  • 79
  • Maybe you should `$scope.$apply()` after variables editing. – morels Mar 23 '16 at 10:40
  • @morels why? What it will do? – Bunyk Mar 23 '16 at 10:57
  • May find this similar SO post helpful http://stackoverflow.com/questions/16168355/angularjs-server-side-validation-and-client-side-forms – Aarati Mar 23 '16 at 13:43
  • Because if the loop you wrote is called in an async callback, the [digest cycle](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$digest) should be noticed that something has changed in the scope. So after the closing curly bracket may be necessary the instruction `$scope.$apply();` – morels Mar 23 '16 at 13:55
  • Why you can't use `$asyncValidators`? – Stepan Kasyanenko Mar 24 '16 at 12:58
  • @StepanKasyanenko because they validate form before submit, and I need after. – Bunyk Mar 24 '16 at 15:26

2 Answers2

0

Try like this

for (var field in errors) {
    $timeout(function(){
        $scope.RegForm.$setValidity(field, false);
        $scope.RegForm[field].$error.server = errors[field].join('\n');
    });
}
Shaxrillo
  • 769
  • 1
  • 7
  • 24
0

Based on the responses suggested in AngularJS - Server side validation and client side forms, we create a directive that will reset the validation after changing the properties of the model.

Live example on jsfiddle.

angular.module('ExampleApp', ['ngMessages'])
  .controller('ExampleController', function($scope, ServerService) {
    $scope.user = {};

    $scope.doSubmit = function(myForm) {
      ServerService.save().then(function(errors) {
        // Set error from server on our form
        angular.forEach(errors, function(error) {
          myForm[error.fieldName].$setValidity(error.error, false);
        });
      });
    };
  })
  //Simulate Ajax call
  .service('ServerService', function($q, $timeout) {
    var errorsFromServer = [{
      fieldName: "firstName",
      error: "serverError"
    }, {
      fieldName: "lastName",
      error: "serverError"
    }, {
      fieldName: "email",
      error: "serverError"
    }, {
      fieldName: "email",
      error: "serverError2"
    }];
    return {
      save: function() {
        return $q.when(errorsFromServer);
      }
    };
  })
  .directive('serverValidation', function() {
    return {
      restrict: "A",
      require: "ngModel",
      scope: {
        ngModel: "=",
        serverValidation: "=" // String or array of strings with name of errors
      },
      link: function(scope, elem, attr, ngModelCtrl) {
        function setValidity(errorName) {
          console.log(errorName);
          ngModelCtrl.$setValidity(errorName, true);
        }
        if (typeof(scope.serverValidation) == "string") {
          console.log(scope.serverValidation);
          scope.arrServerValidation = [scope.serverValidation];
        } else {
          scope.arrServerValidation = scope.serverValidation;
        }
        var firstError = scope.arrServerValidation[0];
        scope.$watch('ngModel', function() {
          // workaround to don't update $setValidity, then changed value of ngModel
          // update $setValidity, only when server-error is true
          if (firstError && ngModelCtrl.$error[firstError])
            angular.forEach(scope.arrServerValidation, setValidity);
        });
      },
    };
  });
.error {
  color: red;
  font-style: italic;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-messages.min.js"></script>

<div ng-app="ExampleApp">
  <div ng-controller="ExampleController">
    <ng-form name="myForm">
      <input ng-model="user.firstName" name="firstName" required server-validation="'serverError'">
      <div ng-messages="myForm.firstName.$error" class="error">
        <div ng-message="required">firstName is required</div>
        <div ng-message="serverError">firstName is server error</div>
      </div>
      <input ng-model="user.lastName" name="lastName" required server-validation="'serverError'">
      <div ng-messages="myForm.lastName.$error" class="error">
        <div ng-message="required">lastName is required</div>
        <div ng-message="serverError">lastName is server error</div>
      </div>
      <input ng-model="user.email" name="email" required server-validation="['serverError','serverError2']">
      <div ng-messages="myForm.email.$error" class="error" multiple="true">
        <div ng-message="required">email is required</div>
        <div ng-message="serverError">email is server error</div>
        <div ng-message="serverError2">email is server error 2</div>
      </div>
      <input ng-disabled="myForm.$invalid" ng-click="doSubmit(myForm)" type="submit">
    </ng-form>
  </div>
</div>
Community
  • 1
  • 1
Stepan Kasyanenko
  • 3,176
  • 1
  • 15
  • 23