AngularJS – 服务器端validation和客户端表单
我正在尝试了解如何执行以下操作:
什么是公认的方式来声明一个表格。 我的理解是你只要在HTML中声明表单,并添加ng-model指令,如下所示:
ng-model="item.name"
什么发送到服务器。 我可以将项目对象作为JSON发送到服务器,并解释它。 然后我可以对对象执行validation。 如果失败,我会抛出一个JSON错误,然后发回到底是什么? 有没有一个可以接受的方法呢? 如何以一种很好的方式将validation错误从服务器推送到客户端?
我真的需要一个例子,但是Angulars文档很难理解。
编辑:似乎我的措辞差劲我的问题。
我知道如何validation客户端,以及如何处理错误/成功作为承诺callback。 我想知道的是将SERVER端错误消息捆绑到客户端的可接受方式。 说我有一个用户名和密码registry单。 我不想轮询服务器的用户名,然后使用Angular来确定存在重复。 我想发送用户名到服务器,validation没有其他帐户存在相同的名称,然后提交表单。 如果发生错误,我该如何发回?
把数据按原样(键和值)推入服务器,并附加一个错误字段,如下所示:
{ ...data... "errors": [ { "context": null, "message": "A detailed error message.", "exceptionName": null } ] }
然后绑定到DOM。
我最近也一直在玩这种东西,我打了这个演示 。 我认为这是你所需要的。
按照正常情况设置您的表单,并使用您要使用的任何特定的客户端validation:
<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>
另请注意,我已经为每个字段添加了一个serverMessage
:
<span ng-show="myForm.firstName.$error.serverMessage">{{myForm.firstName.$error.serverMessage}}</span>
这是一个可定制的消息,从服务器回来,它的工作方式与任何其他错误消息(据我所知)。
这里是控制器:
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]); } } }; }
我假装调用服务器在pretendThisIsOnTheServerAndCalledViaAjax
你可以用ajax调用它来replace它,重点是它只是返回每个字段的validation状态。 在这种简单的情况下,我使用值VALID
来表示该字段是有效的,任何其他值被视为错误消息。 你可能想要更复杂的东西!
从服务器获得validation状态后,只需更新表单中的状态即可。
你可以从范围访问表单,在这种情况下,表单称为myForm
所以$ scope.myForm得到你的表单。 ( 表单控制器的来源在这里,如果你想了解它是如何工作的)。
然后你要告诉表单域是否有效/无效:
$scope.myForm.$setValidity(fieldName, true, $scope.myForm);
要么
$scope.myForm.$setValidity(fieldName, false, $scope.myForm);
我们还需要设置错误消息。 首先使用$ parse获取字段的访问器。 然后从服务器分配值。
var serverMessage = $parse('myForm.'+fieldName+'.$error.serverMessage'); serverMessage.assign($scope, serverResponse[fieldName]);
希望有所帮助
我有类似Derek的解决scheme,在codetunes博客上描述过 。 TL; DR:
-
以类似Derek解决scheme的方式显示错误:
<span ng-show="myForm.fieldName.$error.server">{{errors.fieldName}}</span>
-
添加指令,当用户改变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) }
-
通过将错误消息传递给作用域并告诉该窗体有错误来处理错误:
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(', ')
希望这将是有用的:)
那么,德里克·艾金斯(Derek Ekins)给出的答案是非常好的。 但是:如果禁用ng-disabled="myForm.$invalid"
的提交button – 由于基于服务器的错误状态似乎没有改变,该button不会自动返回到启用状态。 即使您再次编辑表单中的所有字段,以符合有效的input(基于客户端validation)。
默认情况下,表单是正常提交的。 如果您没有为表单中的每个字段提供名称属性,则不会提交正确的数据。 你可以做的是在提交之前捕获表单,并通过ajax自己提交数据。
<form ng-submit="onSubmit(); return false">
然后在你的$scope.onSubmit()
函数中:
$scope.onSubmit = function() { var data = { 'name' : $scope.item.name }; $http.post(url, data) .success(function() { }) .failure(function() { }); };
您也可以通过设置必需的属性来validation数据。
如果你selectngResource,它看起来像这样
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 } ); };
最重要的是,您的HTTP-Response代码必须是4xx才能进入失败callback。
截至2014年7月,AngularJS 1.3增加了新的表单validationfunction。 这包括ngMessages和asyncValidators,因此您现在可以在提交表单之前在每个字段中激活服务器端validation。
Angular 1.3表单validation教程 :
- 在Angular 1.3中驯服表单
- video | 回购 | 演示
参考文献:
- ngMessages指令
- ngModel.NgModelController
我需要一些项目,所以我创build了一个指令。 最后花了一些时间在GitHub上为任何想要一个插件的解决scheme。
https://github.com/webadvanced/ng-remote-validate
特征:
-
解决任何文本或密码input的Ajaxvalidation解决scheme
-
与Angulars一起构buildvalidation,并通过formName.inputName访问cab。$ error.ngRemoteValidate
-
限制服务器请求(默认为400毫秒),可以使用
ng-remote-throttle="550"
-
允许使用
ng-remote-method="GET"
HTTP方法定义(默认POST)用于需要用户input其当前密码以及新密码的更改密码表单的示例用法。更改密码
当前需要错误的当前密码。 请input您的当前帐户密码。
<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>
作为变体
// 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'); } }