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'); } }