0

I have created an angular form that displays input validation errors received from the server. My solution works fine except for one small issue. If I submit the form with no value, after the page loads, I am receiving the correct response from my server i.e. 422, but the validation error is not displayed. If I then start typing a value in the input the validation error flashes and disappears.

I am almost certain that it has something to do with my directive, but I'm not sure how to fix it. This is my current directive code:

var appServices = angular.module('webFrontendApp.directives', []);

appServices.directive('serverError', function(){
  return {
    restrict: 'A',
    require: '?ngModel',
    link: function(scope,element,attrs,ctrl){
      element.on('change keyup', function(){
        scope.$apply(function(){
          ctrl.$setValidity('server', true);
        });
      });
    }
  };
});

I think the issue is with the element.on('change keyup'.... section of this code. That's why the error message flashes when I start typing. Also when I change this to 'change' instead of 'change keyup', the error message is displayed permanently when I start typing.

Does anybody have an idea of how I can display the error message even if I did not type any value into the input before submitting it the first time?

UPDATE AS PER COMMENT

Here is my form:

<form ng-submit="create(memberData)" name="form" novalidate>
  <div class = "row form-group" ng-class = "{ 'has-error' : form.email.$dirty && form.email.$invalid }">
     <input type="text" ng-model="memberData.email" placeholder="janedoe@mail.com"  name="email" class="col-xs-12 form-control" server-error>

     <span class="errors" ng-show="form.email.$dirty && form.email.$invalid">
       <span class="glyphicon glyphicon-remove form-control-feedback"></span>
       <span ng-show="form.email.$error.server">{{errors.email}}</span>
     </span>
  </div>
  <div class="row">
    <button type="submit" class="btn btn-danger col-xs-12">Join Private Beta</button>
  </div>
</form>

And my controller:

$scope.memberData = {};
$scope.create = function() {
    var error, success;
    $scope.errors = {};
    success = function() {
      $scope.memberData = {};
    };    
    error = function(result) {
        angular.forEach(result.data.errors, function(errors, field) {
            $scope.form[field].$setValidity('server', false);
            $scope.errors[field] = errors.join(', ');
        });    
    };



    BetaMember.save({ beta_member: { email: $scope.memberData.email || "" }}).$promise.then(success, error);

};
HermannHH
  • 1,732
  • 1
  • 27
  • 57

2 Answers2

0

Since the form itself doesn't have a $setValidity method, because doens't have an ng-model, and assuming that the server error is not referred to a single field ( in this case a $setValidity method is preferred ), I think that the simplest solution could be this one:

Create a form with some random validation ( this one simply needs at least the username ) and a div that can display a custom server error.

<div ng-controller="AppCtrl">

    <form novalidate name="createForm">

        <div class="inputWrap">

            <input ng-model="name" name="name" type="text" required placeholder="John Doe">

            <span ng-if="createForm.name.$dirty && createForm.name.$invalid">
                Some kind of error!
            </span>

        </div>

        <div ng-if="serverError">
            {{ serverError.message }}
        </div>

        <input
            value="Join Private Beta"
            ng-disabled="createForm.$invalid || serverError" 
            ng-click="create()"
            type="button">

    </form>

</div> 

Then in your controller you can add the create method that deal with YourService ( should return a promise ) and if the response is failure you can create a simple object with a custom server error message inside which will also be usefull to disable the form button, if you need.

var AppCtrl = function($scope, YourService){

    // Properties

    // Shared Properties

    // Methods
    function initCtrl(){}

    // Shared Methods
    $scope.create = function(){

        YourService.makeCall().then(function(response){

            // Success!

            // Reset the custom error
            $scope.serverError = null;

        }, function(error){

            // Do your http call and then if there's an error
            // create the serverError object with a message
            $scope.serverError = {
                message : 'Some error message'
            };

        })

    };

    // Events

    // Init controller
    initCtrl();

};

AppCtrl.$inject = [
    '$scope',
    'YourService'
];

app.controller('AppCtrl', AppCtrl);

I mean it's very simple snippet here, I just wanted to bring an example. Nothing to complex, but you can scale this to something more.

0

It seems that there was an extremely simple workaround. Since I had the filter form.email.$dirty in my view, the error would not be displayed if the user clicked submit without clicking on the form first.

After removing form.email.$dirty and only having form.email.$invalid, it works perfectly. I think this is sufficient in my case, as this validation is dependant on a server response and will not be triggered before the form is submitted. The error object is also cleared up in my controller ensuring that the page does not load an error when it's first loaded.

HermannHH
  • 1,732
  • 1
  • 27
  • 57