AngularJS - проверка на стороне сервера и формы на стороне клиента
Я пытаюсь понять, как сделать следующее:
Каков приемлемый способ объявления формы. Я понимаю, что вы просто объявляете форму в HTML и добавляете такие директивы ng-model:
ng-model="item.name"
Что отправить на сервер. Я могу просто отправить объект объекта на сервер как JSON и интерпретировать его. Затем я могу выполнить проверку на объекте. Если это не удается, я бросаю ошибку JSON и отправляю обратно, что именно? Есть ли принятый способ сделать это? Как я могу с высокой точностью выталкивать ошибки проверки с сервера на клиент?
Мне действительно нужен пример, но документы Angulars довольно трудно понять.
Изменить: Кажется, я неправильно сформулировал свой вопрос.
Я знаю, как проверять клиентскую сторону и как обрабатывать ошибки/успех, как обещания. То, что я хочу знать, является принятым способом связывания сообщений об ошибках со стороны сервера SERVER. Скажем, у меня есть имя пользователя и пароль. Я не хочу опросить сервер для имен пользователей, а затем использовать Angular для определения дубликата. Я хочу отправить имя пользователя на сервер, не проверять, нет ли другой учетной записи с тем же именем, а затем отправить форму. Если возникает ошибка, как мне отправить его обратно?
Как насчет нажатия на данные на сервере как есть (ключи и значения) с полем ошибок, добавленным так:
{
...data...
"errors": [
{
"context": null,
"message": "A detailed error message.",
"exceptionName": null
}
]
}
Затем привязка к DOM.
Ответы
Ответ 1
В последнее время я тоже общался с этим, и я вывел эту демонстрацию. Я думаю, что он делает то, что вам нужно.
Настройте свою форму в соответствии с нормами с любыми проверками на стороне клиента, которые вы хотите использовать:
<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 получает форму. (Источник для контроллера формы здесь, если вы хотите прочитать, как он работает).
Затем вы хотите сообщить форме, является ли поле действительным/недействительным:
$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]);
Надеюсь, что поможет
Ответ 2
У меня есть аналогичное решение, такое как Derek, описанное в блоге codetunes. TL; ДР:
-
отображает ошибку аналогично решению Дерека:
<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(', ')
Надеюсь, это было бы полезно:)
Ответ 3
Ну, ответ, который дал Дерек Экинс, очень приятно работать. Но: Если вы отключите кнопку отправки с помощью ng-disabled="myForm.$invalid"
- кнопка не будет автоматически возвращаться к включенному, поскольку состояние ошибки на сервере, похоже, не изменяется. Даже если вы снова редактируете ВСЕ поля в форме, чтобы соответствовать действительным входам (на основе проверки на стороне клиента).
Ответ 4
По умолчанию форма отправляется в обычном режиме. Если вы не укажете свойство имени для каждого поля в форме, оно не представит правильные данные. Что вы можете сделать, так это захватить форму перед ее отправкой и передать эти данные через 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() {
});
};
Вы также можете проверить данные, настроив необходимые атрибуты.
Ответ 5
Если вы выберете 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, чтобы ввести обратный вызов сбоя.
Ответ 6
По состоянию на июль 2014 года AngularJS 1.3 добавила новые функции проверки формы. Это включает в себя ngMessages и asyncValidators, чтобы вы могли теперь выполнить проверку на стороне сервера на поле перед отправкой формы.
Angular 1.3 Учебное пособие по проверке формы:
Литература:
Ответ 7
Мне нужно было это в нескольких проектах, поэтому я создал директиву. Наконец, настало время, чтобы поместить его в GitHub для тех, кто хочет отказаться от решения.
https://github.com/webadvanced/ng-remote-validate
Особенности:
-
Падение в решении для проверки Ajax любого ввода текста или пароля
-
Работы с Угловыми строками в валидации и кэше доступны в формеName.inputName. $error.ngRemoteValidate
-
Отправляет запросы сервера (по умолчанию 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>
Ответ 8
В качестве варианта
// 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');
}
}