我想了解如何做以下事情:
声明表单的可接受方式是什么。我的理解是你只需在HTML中声明表单,并添加如下的ng-model指令:
ng-model="item.name"
要发送到服务器的内容。我可以将项目对象作为JSON发送到服务器,并解释它。然后我可以对对象执行验证。如果失败了,我会抛出一个JSON错误,然后发回确切的内容?这是否有可接受的方式?如何以一种很好的方式将验证错误从服务器推送到客户端?
我真的需要一个例子,但Angulars文档很难理解。
编辑:看来我的问题很糟糕。我知道如何验证客户端,以及如何处理错误/成功作为承诺回调。我想知道的是将SERVER端错误消息捆绑到客户端的可接受方式。假设我有一个用户名和密码注册表单。我不想在服务器上轮询用户名,然后使用Angular来确定是否存在重复。我想将用户名发送到服务器,验证没有其他帐户存在同名,然后提交表单。如果发生错误,我该如何发回?
如何将数据按原样(键和值)推送到服务器,并附加错误字段,如下所示:
{
...data...
"errors": [
{
"context": null,
"message": "A detailed error message.",
"exceptionName": null
}
]
}
然后绑定到DOM。
答案 0 :(得分:64)
我最近也一直在玩这种事情,我已经打倒了this demo。我认为它可以满足您的需求。
使用您要使用的任何特定客户端验证,按照正常方式设置表单:
<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调用替换它,关键是它只返回每个字段的验证状态。在这个简单的例子中,我使用值VALID
来表示该字段是有效的,任何其他值都被视为错误消息。你可能想要更复杂的东西!
从服务器获得验证状态后,您只需更新表单中的状态即可。
您可以从范围访问表单,在这种情况下,表单称为myForm
,因此$ scope.myForm会为您提供表单。 (form controller is here的来源,如果你想了解它是如何工作的。)
然后,您想告诉表单该字段是否有效:
$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]);
希望有所帮助
答案 1 :(得分:17)
我有与Derek类似的解决方案,在codetunes blog上有描述。 TL; DR:
以与Derek解决方案类似的方式显示错误:
<span ng-show="myForm.fieldName.$error.server">{{errors.fieldName}}</span>
添加指令,当用户更改输入时会清除错误:
<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(', ')
希望它有用:)
答案 2 :(得分:5)
嗯,Derek Ekins给出的答案是非常好的工作。但是:如果您使用ng-disabled="myForm.$invalid"
禁用提交按钮 - 该按钮将不会自动返回启用状态,因为基于服务器的错误状态似乎没有更改。即使您再次编辑表单中的所有字段以符合有效输入(基于客户端验证)也不会。
答案 3 :(得分:3)
默认情况下,表单正常提交。如果您没有为表单中的每个字段提供名称属性,那么它将无法提交正确的数据。您可以做的是在提交表单之前捕获表单,并通过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() {
});
};
您还可以通过设置必需的属性来验证数据。
答案 4 :(得分:3)
如果您选择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
}
);
};
最重要的是,您的HTTP响应代码必须是4xx才能进入故障回调。
答案 5 :(得分:3)
截至2014年7月,AngularJS 1.3增加了新的表单验证功能。这包括ngMessages和asyncValidators,因此您现在可以在提交表单之前为每个字段触发服务器端验证。
Angular 1.3表单验证教程:
<强>参考文献:强>
答案 6 :(得分:2)
我在一些项目中需要这个,所以我创建了一个指令。最后花了一点时间把它放在GitHub上,供那些想要一个直接解决方案的人使用。
https://github.com/webadvanced/ng-remote-validate
功能强>
为任何文本或密码输入的Ajax验证提供解决方案
使用Angulars构建验证,并可通过formName.inputName访问cab。$ error.ngRemoteValidate
Throttles服务器请求(默认为400毫秒),可以使用ng-remote-throttle="550"
允许使用ng-remote-method="GET"
的HTTP方法定义(默认POST)
更改密码表单的示例用法,要求用户输入其当前密码以及新密码。
<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>
答案 7 :(得分:1)
作为变种
// 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');
}
}