24

I have have my angular app validating a sign-up form. On submit, the server also validates the data. I'm outputting error messages in angular using ng-messages.

Here is a shortened version of my form, which works perfectly so far.

<form name="signUpForm" novalidate data-ng-submit="attemptSignUp()">
    <label for="firstName">First name</label>
    <input type="email" name="email" id="email" required data-ng-model="data.user.email" />
    <div class="error" data-ng-messages="signUpForm.email.$error" data-ng-show="signUpForm.$submitted" data-ng-cloak>
        <p data-ng-message="required">Please provide your email</p>
    </div>
</form>

The server verifies the email address is unique, and if not, returns a 422 error (from Laravel 5), with an array of errors.

[
    'email' => 'This email is already in use'
]

I'd like to merge in this, and any other messages sent back from the server into their relevant ng-messages block. Any idea how I could accomplish it?

4 Answers 4

27
+50

A simple solution is to have two arrays. One for client side and one for server side errors which is populated in your controller. You can hide the server side errors if client errors exists or opposit to avoid double messages.

The reason I choose to have two arrays instead of populating the forms array is that the JavaScript controller should not know or be dependent on the structure of the HTML. The HTML AngularJS template should be bound to the controller, not opposit.

<form name="signUpForm" novalidate data-ng-submit="attemptSignUp()">
    <label for="email">E-mail
        <input type="email" name="email" id="email" required data-ng-model="data.user.email" />
        <div class="error" data-ng-messages="signUpForm.email.$error" data-ng-show="signUpForm.$submitted" data-ng-cloak>
            <p data-ng-message="required">Please provide your email</p>
        </div>
        <div class="error" data-ng-messages="serverErrors" data-ng-show="signUpForm.$submitted" data-ng-cloak>
            <p data-ng-message="emailexists">Email already exists</p>
        </div>
    </label
</form>

A note on the label: Users using screen-readers will not get your error messages read out loud to them if they are not wrapped inside the label.

Sign up to request clarification or add additional context in comments.

Comments

8

Well, this is not the most elegant solution since you really should leverage the asyncValidators in angular 1.3.x and then create your custom validation directives.

Resources

http://plnkr.co/edit/s4jJAOqehBkFUC9osMsy?p=preview found in the post by this guy.

Possibly here http://odetocode.com/blogs/scott/archive/2014/10/16/working-with-validators-and-messages-in-angularjs.aspx

And of course in the docs

But be cautious as this is not in any way a complete example ready to be used. It's mostly here for demo purpose and to sort of give you an idea where to start. I have not bothered with clearing any previous errors, revalidating the form or taken into account other validation errors.

Awesomeness

Imagine your controller looks like this

$scope.serverValidations = {};
$scope.attemptSignUp = function(){

    Api.validateEmail($scope.email).then(angular.noop, function(data){

      $scope.serverValidations = data
      for(prop in $scope.serverValidations){
          if($scope.signUpForm[prop]){
            angular.forEach($scope.serverValidations[prop],function(validation){
                $scope.signUpForm[prop].$setValidity(validation.type, false);
            });
          }
      }
    });
}

and your response data containing validation errors look like this

{
  email:[
     {type:'unique', message:'This email is already in use'}
  ],
  name:[
     {type:'maxlength', message:'Your name is to long, get a new one :)'}
  ]
};

Then in your HTML you could do like this

<div class="error" data-ng-messages="signUpForm.name.$error" data-ng-cloak="">
    <p data-ng-message="required">You don't have a name?</p>
    <p ng-repeat="validation in serverValidations['name']" ng-message="{{validation.type}}">{{validation.message}}</p>
</div>

Here's a dirty Codepen for you: http://codepen.io/anon/pen/yyzMgG?editors=101 When you press submit, after 2 seconds (the time it takes to hit the fake server) your server validations are presented.

Comments

7

First of all you should set validity and error messages

      $scope.formErrors = {};
      angular.forEach(errors, function(data, name) {
        if (!vm.register_form[name]) {
          return;
        }
        $scope.formErrors[name] = data.message;
        //this will set errors->server to invalid state
        $scope.register_form[name].$setValidity('server', false);
      });

The next step will be rendering by ng-messages

      <div ng-messages="register_form.email.$error">
        <div ng-message="required">Email is required</div>
        <div ng-message="email">Invalid email</div>
        <div ng-message="server">{{formErrors.email}}</div>
      </div>

Comments

-5

I had a similar question, but the solutions did not work for me. What I did, and it is/was a hack/work around, was to send different errorcodes, and set a case statement.

1 Comment

Not a complete answer as no solution is provided

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.