AngularJS - server side validation and client side form validation

I am trying to figure out how to do the following:

What is an acceptable way to declare a form. I understand that you simply declare the form in HTML and add ng-model directives like this:

ng-model="item.name" 

What to send to the server. I can simply send the object of the object to the server as JSON and interpret it. Then I can perform a check on the object. If this fails, I throw a JSON error and send it back, what exactly? Is there an accepted way to do this? How can I push verification errors from server to client with high accuracy?

I really need an example, but Angulars docs are pretty hard to understand.

Edit: It seems I mis-formulated my question.

I know how to check the client side and how to handle errors / success, like promises. What I want to know is the accepted way of binding error messages from the SERVER server. Say I have a username and password. I don't want to poll the server for usernames, and then use Angular to determine the duplicate. I want to send the username to the server, not check if there is another account with the same name, and then submit the form. If an error occurs, how do I send it back?

How about clicking on the data on the server as it is (keys and values) with an error field added like this:

 { ...data... "errors": [ { "context": null, "message": "A detailed error message.", "exceptionName": null } ] } 

Then binding to the DOM.

+56
json angularjs
Apr 23 '13 at 11:35
source share
8 answers

Recently, I also talked to this, and I put out this demonstration . I think he does what you need.

Customize your form in accordance with the standards with any client-side checks you want to use:

 <div ng-controller="MyCtrl"> <form name="myForm" onsubmit="return false;"> <div> <input type="text" placeholder="First name" name="firstName" ng-model="firstName" required="true" /> <span ng-show="myForm.firstName.$dirty && myForm.firstName.$error.required">You must enter a value here</span> <span ng-show="myForm.firstName.$error.serverMessage">{{myForm.firstName.$error.serverMessage}}</span> </div> <div> <input type="text" placeholder="Last name" name="lastName" ng-model="lastName"/> <span ng-show="myForm.lastName.$error.serverMessage">{{myForm.lastName.$error.serverMessage}}</span> </div> <button ng-click="submit()">Submit</button> </form> </div> 

Note also that I added serverMessage for each field:

 <span ng-show="myForm.firstName.$error.serverMessage">{{myForm.firstName.$error.serverMessage}}</span> 

This is a custom message returned from the server, and it works just like any other error message (as far as I can tell).

Here is the controller:

 function MyCtrl($scope, $parse) { var pretendThisIsOnTheServerAndCalledViaAjax = function(){ var fieldState = {firstName: 'VALID', lastName: 'VALID'}; var allowedNames = ['Bob', 'Jill', 'Murray', 'Sally']; if (allowedNames.indexOf($scope.firstName) == -1) fieldState.firstName = 'Allowed values are: ' + allowedNames.join(','); if ($scope.lastName == $scope.firstName) fieldState.lastName = 'Your last name must be different from your first name'; return fieldState; }; $scope.submit = function(){ var serverResponse = pretendThisIsOnTheServerAndCalledViaAjax(); for (var fieldName in serverResponse) { var message = serverResponse[fieldName]; var serverMessage = $parse('myForm.'+fieldName+'.$error.serverMessage'); if (message == 'VALID') { $scope.myForm.$setValidity(fieldName, true, $scope.myForm); serverMessage.assign($scope, undefined); } else { $scope.myForm.$setValidity(fieldName, false, $scope.myForm); serverMessage.assign($scope, serverResponse[fieldName]); } } }; } 

I pretend that you are calling the server in pretendThisIsOnTheServerAndCalledViaAjax , you can replace it with an ajax call, period - this just returns the verification status for each field. In this simple case, I use the VALID value to indicate that the field is valid, any other value is treated as an error message. You may need something more complicated!

Once you have the validation status from the server, you just need to update the status in your form.

You can access the form from scope, in which case the form is called myForm , so $ scope.myForm gets the form. (The source for the form controller is here if you want to read how it works).

Then you want to tell the form whether the field is valid / invalid:

 $scope.myForm.$setValidity(fieldName, true, $scope.myForm); 

or

 $scope.myForm.$setValidity(fieldName, false, $scope.myForm); 

We also need to set an error message. First of all, access the field accessory using $ parse. Then assign the value from the server.

 var serverMessage = $parse('myForm.'+fieldName+'.$error.serverMessage'); serverMessage.assign($scope, serverResponse[fieldName]); 

Hope that helps

+63
May 10 '13 at 8:30
source share

I have a similar solution like Derek described on the codetunes blog . TL; DR:

  • displays an error similar to Derek's solution:

     <span ng-show="myForm.fieldName.$error.server">{{errors.fieldName}}</span> 

  • add a directive that will clear the error when the user changes the input:

     <input type="text" name="fieldName" ng-model="model.fieldName" server-error /> angular.module('app').directive 'serverError', -> { restrict: 'A' require: '?ngModel' link: (scope, element, attrs, ctrl) -> element.on 'change', -> scope.$apply -> ctrl.$setValidity('server', true) } 
  • Handle the error by sending the error message to the scope and reporting that the form has an error:

     errorCallback = (result) -> # server will return something like: # { errors: { name: ["Must be unique"] } } angular.forEach result.data.errors, (errors, field) -> # tell the form that field is invalid $scope.form[field].$setValidity('server', false) # keep the error messages from the server $scope.errors[field] = errors.join(', ') 

Hope this would be helpful :)

+16
Sep 30 '13 at 11:50
source share

Well, the answer Derek Akins gave is a pleasure to work with. But: If you disable the submit button using ng-disabled="myForm.$invalid" , the button will not automatically return to enabled, because the error state on the server does not seem to change. Even if you edit ALL the fields on the form again to match the valid inputs (based on client-side validation).

+5
Jun 27 '13 at 10:21
source share

By default, the form is submitted as usual. If you do not specify a name property for each field in the form, it will not provide the correct data. What you can do is grab the form before submitting it and pass this data through ajax.

 <form ng-submit="onSubmit(); return false"> 

And then in your $scope.onSubmit() function:

 $scope.onSubmit = function() { var data = { 'name' : $scope.item.name }; $http.post(url, data) .success(function() { }) .failure(function() { }); }; 

You can also check the data by setting the necessary attributes.

+3
Apr 23 '13 at 12:26
source share

If you select ngResource, it will look like

 var Item = $resource('/items/'); $scope.item = new Item(); $scope.submit = function(){ $scope.item.$save( function(data) { //Yahooooo :) }, function(response) { //oh noooo :( //I'm not sure, but your custom json Response should be stick in response.data, just inspect the response object } ); }; 

Most importantly, your HTTP response code must be 4xx in order to enter a failure callback.

+3
Apr 23 '13 at 14:40
source share

As of July 2014, AngularJS 1.3 has added new form validation features. This includes ngMessages and asyncValidators so that you can now perform server side validation on the field before submitting the form.

Angular 1.3 Form Validation Tutorial :

Literature:

+3
Jun 02 '15 at 5:25
source share

I needed this in several projects, so I created a directive. Finally, the time has come to put it on GitHub for those who want to abandon the decision.

https://github.com/webadvanced/ng-remote-validate

Features:

  • Crash in Ajax solution for any text or password input

  • Works with Corner strings in validation and cache are available in the form Name.inputName. $ error.ngRemoteValidate

  • Sends server requests (400 ms by default) and can be set using ng-remote-throttle="550"

  • Allows the definition of the HTTP method (default POST) using ng-remote-method="GET" An example of using a password form to change, in which the user must enter his current password, as well as a new password .:

    Change password

    Current Required Invalid current password. Enter your current password.
     <label for="newPassword">New</label> <input type="password" name="newPassword" placeholder="New password" ng-model="password.new" required> <label for="confirmPassword">Confirm</label> <input ng-disabled="" type="password" name="confirmPassword" placeholder="Confirm password" ng-model="password.confirm" ng-match="password.new" required> <span ng-show="changePasswordForm.confirmPassword.$error.match"> New and confirm do not match </span> <div> <button type="submit" ng-disabled="changePasswordForm.$invalid" ng-click="changePassword(password.new, changePasswordForm);reset();"> Change password </button> </div> 
+2
Apr 24 '14 at 23:11
source share

As an option

 // ES6 form controller class class FormCtrl { constructor($scope, SomeApiService) { this.$scope = $scope; this.someApiService = SomeApiService; this.formData = {}; } submit(form) { if (form.$valid) { this.someApiService .save(this.formData) .then(() => { // handle success // reset form form.$setPristine(); form.$setUntouched(); // clear data this.formData = {}; }) .catch((result) => { // handle error if (result.status === 400) { this.handleServerValidationErrors(form, result.data && result.data.errors) } else {// TODO: handle other errors} }) } } handleServerValidationErrors(form, errors) { // form field to model map // add fields with input name different from name in model // example: <input type="text" name="bCategory" ng-model="user.categoryId"/> var map = { categoryId: 'bCategory', // other }; if (errors && errors.length) { // handle form fields errors separately angular.forEach(errors, (error) => { let formFieldName = map[error.field] || error.field; let formField = form[formFieldName]; let formFieldWatcher; if (formField) { // tell the form that field is invalid formField.$setValidity('server', false); // waits for any changes on the input // and when they happen it invalidates the server error. formFieldWatcher = this.$scope.$watch(() => formField.$viewValue, (newValue, oldValue) => { if (newValue === oldValue) { return; } // clean up the server error formField.$setValidity('server', true); // clean up form field watcher if (formFieldWatcher) { formFieldWatcher(); formFieldWatcher = null; } }); } }); } else { // TODO: handle form validation alert('Invalid form data'); } } 
+1
Jun 18 '15 at 10:59
source share



All Articles