json AngularJS - 服务器端验证和客户端表单
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16168355/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
AngularJS - Server side validation and client side forms
提问by Dominic Bou-Samra
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:
声明表单的公认方式是什么。我的理解是您只需在 HTML 中声明表单,然后添加 ng-model 指令,如下所示:
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?
向服务器发送什么。我可以将 item 对象作为 JSON 发送到服务器,并对其进行解释。然后我可以对对象执行验证。如果失败,我会抛出一个 JSON 错误,然后发回究竟是什么?有没有一种可接受的方式来做到这一点?如何以一种很好的方式将验证错误从服务器推送到客户端?
I really need an example, but Angulars docs are pretty difficult to understand.
我真的需要一个例子,但 Angulars 文档很难理解。
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?
我知道如何验证客户端,以及如何处理错误/成功作为承诺回调。我想知道的是将 SERVER 端错误消息捆绑到客户端的可接受方式。假设我有一个用户名和密码注册表单。我不想轮询服务器以获取用户名,然后使用 Angular 来确定是否存在重复项。我想将用户名发送到服务器,验证不存在同名的其他帐户,然后提交表单。如果发生错误,我如何将其发回?
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.
然后绑定到DOM。
回答by Derek Ekins
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 serverMessagefor each field:
另请注意,我serverMessage为每个字段添加了一个:
<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 pretendThisIsOnTheServerAndCalledViaAjaxyou 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 VALIDto indicate that the field is valid, any other value is treated as an error message. You may want something more sophisticated!
我假装调用服务器,pretendThisIsOnTheServerAndCalledViaAjax你可以用 ajax 调用替换它,重点是它只返回每个字段的验证状态。在这个简单的例子中,我使用该值VALID来指示该字段是有效的,任何其他值都被视为错误消息。你可能想要更复杂的东西!
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 myFormso $scope.myForm gets you the form. (Source for the form controller is hereif you want to read up on how it works).
您可以从范围访问表单,在这种情况下,表单被称为myForm$scope.myForm 为您获取表单。(如果您想了解它是如何工作的,那么表单控制器的来源在这里)。
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.
我们还需要设置错误信息。首先使用 $parse 获取字段的访问器。然后从服务器分配值。
var serverMessage = $parse('myForm.'+fieldName+'.$error.serverMessage');
serverMessage.assign($scope, serverResponse[fieldName]);
Hope that helps
希望有帮助
回答by Jan Dudulski
I've got similar solution as Derek, described on codetunes blog. TL;DR:
我有与 Derek 类似的解决方案,在codetunes blog上有所描述。特尔;博士:
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(', ')
以与 Derek 的解决方案类似的方式显示错误:
<span ng-show="myForm.fieldName.$error.server">{{errors.fieldName}}</span>add 指令将在用户更改输入时清除错误:
<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(', ')
Hope it would be useful :)
希望它会有用:)
回答by Yosh
Well, the Answer Derek Ekins gave is very nice to work on. But: If you disable the submit button with ng-disabled="myForm.$invalid"- the button will not automatically go back to enabled as the server-based error state doesn't seem to be changed. Not even if you edit ALL fields in a form again to comply with valid inputs (based on client side validation).
好吧,Derek Ekins 给出的答案非常好用。但是:如果您禁用提交按钮ng-disabled="myForm.$invalid"- 该按钮将不会自动恢复为启用状态,因为基于服务器的错误状态似乎没有改变。即使您再次编辑表单中的所有字段以符合有效输入(基于客户端验证),也不会。
回答by matsko
By default, the form is submitted normally. If you don't provide a name property for each field in the form then it won't submit the correct data. What you can do is capture the form before it submitted and submit that data yourself via ajax.
默认情况下,表单正常提交。如果您没有为表单中的每个字段提供 name 属性,则它不会提交正确的数据。您可以做的是在提交之前捕获表单并通过ajax自己提交该数据。
<form ng-submit="onSubmit(); return false">
And then in your $scope.onSubmit()function:
然后在你的$scope.onSubmit()函数中:
$scope.onSubmit = function() {
var data = {
'name' : $scope.item.name
};
$http.post(url, data)
.success(function() {
})
.failure(function() {
});
};
You can also validate the data by setting up required attributes.
您还可以通过设置必需的属性来验证数据。
回答by kfis
If you choose ngResource, it would look like this
如果你选择 ngResource,它看起来像这样
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
}
);
};
The most important thing is, that your HTTP-Response code have to be a 4xx to enter the failure callback.
最重要的是,您的 HTTP 响应代码必须是 4xx 才能进入失败回调。
回答by Tony O'Hagan
As of July 2014, AngularJS 1.3 has added new form validation features. This includes ngMessages and asyncValidators so you can now fire server side validation per field prior to submitting the form.
截至 2014 年 7 月,AngularJS 1.3 添加了新的表单验证功能。这包括 ngMessages 和 asyncValidators,因此您现在可以在提交表单之前对每个字段进行服务器端验证。
Angular 1.3 Form validation tutorial:
Angular 1.3 表单验证教程:
References:
参考:
回答by Paul
I needed this in a few projects so I created a directive. Finally took a moment to put it up on GitHub for anyone who wants a drop-in solution.
我在几个项目中需要这个,所以我创建了一个指令。最后花点时间把它放在 GitHub 上,供任何想要插入式解决方案的人使用。
https://github.com/webadvanced/ng-remote-validate
https://github.com/webadvanced/ng-remote-validate
Features:
特征:
Drop in solution for Ajax validation of any text or password input
Works with Angulars build in validation and cab be accessed at formName.inputName.$error.ngRemoteValidate
Throttles server requests (default 400ms) and can be set with
ng-remote-throttle="550"Allows HTTP method definition (default POST) with
ng-remote-method="GET"Example usage for a change password form that requires the user to enter their current password as well as the new password.:Change password
Current Required Incorrect current password. Please enter your current account 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>
任何文本或密码输入的 Ajax 验证解决方案
使用 Angulars 内置验证,可在 formName.inputName.$error.ngRemoteValidate 访问 cab
限制服务器请求(默认 400 毫秒)并且可以设置为
ng-remote-throttle="550"允许 HTTP 方法定义(默认 POST)与
ng-remote-method="GET"更改密码表单的示例用法,该表单要求用户输入其当前密码以及新密码。:更改密码
当前需要 当前密码不正确。请输入您当前的帐户密码。<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>
回答by Bohdan Lyzanets
As variant
作为变体
// 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');
}
}

