Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to display server errors in Angularjs with ng-messages

Tags:

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?

like image 258
Leon Avatar asked Jan 26 '15 19:01

Leon


People also ask

How to display error message in AngularJS?

in controller: $scope. errors = []; $scope. hasError = false; $scope.

What is Ng error in Angularjs?

Overview. ngMessages is a directive that is designed to show and hide messages based on the state of a key/value object that it listens on. The directive itself complements error message reporting with the ngModel $error object (which stores a key/value state of validation errors).

What is $dirty in Angularjs?

$dirty means the user has changed the input value, $invalid means the address itself is invalid. Therefore the error is only shown if the user has actively changed the input value to either an empty or invalid value.


2 Answers

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.

like image 169
jornare Avatar answered Nov 26 '22 13:11

jornare


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.

like image 36
mengstrom Avatar answered Nov 26 '22 13:11

mengstrom