I am trying to understand how to do the following things:
What is the accepted way of declaring a form. My understanding is you just declare the form in HTML, and add ng-model directives like so:
ng-model="item.name"
What to send to the server. I can just send the item object to the server as JSON, and interpret it. Then I can perform validation on object. If it fails, I throw a JSON error, and send back what exactly? Is there an accepted way of doing this? How do I push validation errors from the server to the client in a nice way?
I really need an example, but Angulars docs are pretty difficult to understand.
Edit: It seems I've phrased my question poorly.
I know how to validate client side, and how to handle error/success as promise callbacks. What I want to know, is the accepted way of bundling SERVER side error messages to the client. Say I have a username and password signup form. I don't want to poll the server for usernames and then use Angular to determine a duplicate exists. I want to send the username to the server, validate no other account exists with the same name, and then submit form. If an error occurs, how do I send it back?
What about pushing the data to the server as is (keys and values) with an error field appended like so:
{ ...data... "errors": [ { "context": null, "message": "A detailed error message.", "exceptionName": null } ] }
Then binding to the DOM.
While client-side validation is faster and favors user experience, it should not be used in isolation because client-side validation relies on the browser (which can be tricked). Client-side validation should always be used along with server-side validation as the latter is more reliable.
AngularJS offers client-side form validation. AngularJS monitors the state of the form and input fields (input, textarea, select), and lets you notify the user about the current state. AngularJS also holds information about whether they have been touched, or modified, or not.
Angular JS is a client side JavaScript framework for data binding.
The form instance can optionally be published into the scope using the name attribute. So to check form validity, you can check value of $scope. yourformname. $valid property of scope.
I've got similar solution as Derek, described on codetunes blog. TL;DR:
display an error in similar way as in Derek's solution:
<span ng-show="myForm.fieldName.$error.server">{{errors.fieldName}}</span>
add directive which would clean up an error when user change 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 an error by passing the error message to the scope and telling that 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 it would be useful :)
I've also been playing around with this kind of thing recently and I've knocked up this demo. I think it does what you need.
Setup your form as per normal with any particular client side validations 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 I have added a serverMessage
for each field:
<span ng-show="myForm.firstName.$error.serverMessage">{{myForm.firstName.$error.serverMessage}}</span>
This is a customisable message that comes back from the server and it works the same way as 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 am pretending to call the server in pretendThisIsOnTheServerAndCalledViaAjax
you can replace it with an ajax call, the point is it just returns the validation state for each field. In this simple case I am using the value VALID
to indicate that the field is valid, any other value is treated as an error message. You may want something more sophisticated!
Once you have the validation state from the server you just need to update the state in your form.
You can access the form from scope, in this case the form is called myForm
so $scope.myForm gets you the form. (Source for the form controller is here if you want to read up on how it works).
You then 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 the error message. First of all get the accessor for the field using $parse. Then assign the value from the server.
var serverMessage = $parse('myForm.'+fieldName+'.$error.serverMessage'); serverMessage.assign($scope, serverResponse[fieldName]);
Hope that helps
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With