AngularJS: интеграция с проверкой на стороне сервера
У меня есть приложение angular, содержащее кнопку сохранения, взятую из примеров:
<button ng-click="save" ng-disabled="form.$invalid">SAVE</button>
Это отлично подходит для проверки на стороне клиента, потому что form.$invalid
становится ложным, поскольку пользователь исправляет проблемы, но у меня есть поле электронной почты, которое установлено неверно, если другой пользователь зарегистрирован с тем же адресом электронной почты.
Как только я установил свое поле электронной почты недействительным, я не могу отправить форму, и у пользователя нет возможности исправить эту ошибку проверки. Поэтому теперь я больше не могу использовать form.$invalid
для отключения моей кнопки отправки.
Должен быть лучший способ
Ответы
Ответ 1
Мне нужно было это в нескольких проектах, поэтому я создал директиву. Наконец, настало время, чтобы поместить его в GitHub для тех, кто хочет отказаться от решения.
https://github.com/webadvanced/ng-remote-validate
Особенности:
-
Падение в решении для проверки Ajax любого ввода текста или пароля
-
Работы с Угловыми строками в валидации и кэше доступны в формеName.inputName. $error.ngRemoteValidate
-
Отправляет запросы сервера (по умолчанию 400 мс) и может быть установлен с помощью ng-remote-throttle="550"
-
Разрешает определение метода HTTP (по умолчанию POST) с помощью ng-remote-method="GET"
Пример использования формы смены пароля, которая требует, чтобы пользователь вводил свой текущий пароль, а также новый пароль.:
<h3>Change password</h3>
<form name="changePasswordForm">
<label for="currentPassword">Current</label>
<input type="password"
name="currentPassword"
placeholder="Current password"
ng-model="password.current"
ng-remote-validate="/customer/validpassword"
required>
<span ng-show="changePasswordForm.currentPassword.$error.required && changePasswordForm.confirmPassword.$dirty">
Required
</span>
<span ng-show="changePasswordForm.currentPassword.$error.ngRemoteValidate">
Incorrect current password. Please enter your current account password.
</span>
<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>
</form>
Ответ 2
Это другой случай, когда пользовательская директива - ваш друг. Вы захотите создать директиву и ввести в нее $http или $resource, чтобы сделать обратный вызов на сервере во время проверки.
Некоторые псевдокоды для пользовательской директивы:
app.directive('uniqueEmail', function($http) {
var toId;
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, elem, attr, ctrl) {
//when the scope changes, check the email.
scope.$watch(attr.ngModel, function(value) {
// if there was a previous attempt, stop it.
if(toId) clearTimeout(toId);
// start a new attempt with a delay to keep it from
// getting too "chatty".
toId = setTimeout(function(){
// call to some API that returns { isValid: true } or { isValid: false }
$http.get('/Is/My/EmailValid?email=' + value).success(function(data) {
//set the validity of the field
ctrl.$setValidity('uniqueEmail', data.isValid);
});
}, 200);
})
}
}
});
И вот как вы будете использовать его в разметке:
<input type="email" ng-model="userEmail" name="userEmail" required unique-email/>
<span ng-show="myFormName.userEmail.$error.uniqueEmail">Email is not unique.</span>
EDIT: небольшое объяснение того, что происходит выше.
- Когда вы обновляете значение на входе, оно обновляет $scope.userEmail
- У директивы есть $watch на $scope.userEmail, который он установил в ней, связывая функцию.
- Когда срабатывает $watch, он вызывает вызов через сервер через $http ajax call, передавая сообщение
- Сервер будет проверять адрес электронной почты и возвращать простой ответ типа '{isValid: true}
- этот ответ используется для $setValidity элемента управления.
- В разметке есть набор ng-show, который будет отображаться только в том случае, если значение свойства uniqueEmail false.
... пользователю, что означает:
- Введите адрес электронной почты.
- небольшая пауза.
- Сообщение "Электронная почта не уникально" отображает "реальное время", если адрес электронной почты не уникален.
EDIT2: Это также позволяет использовать форму. $invalid, чтобы отключить кнопку отправки.
Ответ 3
Я создал плункер с решением, которое идеально подходит для меня. Он использует настраиваемую директиву, но на всей форме, а не на отдельном поле.
http://plnkr.co/edit/HnF90JOYaz47r8zaH5JY
Я бы не рекомендовал отключить кнопку отправки для проверки сервера.
Ответ 4
Ok. Если кому-то нужна рабочая версия, она находится здесь:
Из документа doc:
$apply() is used to enter Angular execution context from JavaScript
(Keep in mind that in most places (controllers, services)
$apply has already been called for you by the directive which is handling the event.)
Это заставило меня подумать, что нам не нужно: $scope.$apply(function(s) {
иначе он будет жаловаться на $digest
app.directive('uniqueName', function($http) {
var toId;
return {
require: 'ngModel',
link: function(scope, elem, attr, ctrl) {
//when the scope changes, check the name.
scope.$watch(attr.ngModel, function(value) {
// if there was a previous attempt, stop it.
if(toId) clearTimeout(toId);
// start a new attempt with a delay to keep it from
// getting too "chatty".
toId = setTimeout(function(){
// call to some API that returns { isValid: true } or { isValid: false }
$http.get('/rest/isUerExist/' + value).success(function(data) {
//set the validity of the field
if (data == "true") {
ctrl.$setValidity('uniqueName', false);
} else if (data == "false") {
ctrl.$setValidity('uniqueName', true);
}
}).error(function(data, status, headers, config) {
console.log("something wrong")
});
}, 200);
})
}
}
});
HTML:
<div ng-controller="UniqueFormController">
<form name="uniqueNameForm" novalidate ng-submit="submitForm()">
<label name="name"></label>
<input type="text" ng-model="name" name="name" unique-name> <!-- 'unique-name' because of the name-convention -->
<span ng-show="uniqueNameForm.name.$error.uniqueName">Name is not unique.</span>
<input type="submit">
</form>
</div>
Контроллер может выглядеть так:
app.controller("UniqueFormController", function($scope) {
$scope.name = "Bob"
})
Ответ 5
Благодаря ответам с этой страницы вы узнали о https://github.com/webadvanced/ng-remote-validate
Опционные директивы, которые немного меньше, чем мне не нравятся, поскольку каждое поле должно писать директиву.
Модуль одинаковый - универсальное решение.
Но в модулях мне что-то не хватает - проверьте поле для нескольких правил.
Затем я просто изменил модуль https://github.com/borodatych/ngRemoteValidate
Извинения за Russian README, в конечном счете, изменятся.
Я спешу поделиться неожиданностью с кем-то с той же проблемой.
Да, и мы собрались здесь для этого...
Загрузка:
<script type="text/javascript" src="../your/path/remoteValidate.js"></script>
Включить:
var app = angular.module( 'myApp', [ 'remoteValidate' ] );
HTML
<input type="text" name="login"
ng-model="user.login"
remote-validate="( '/ajax/validation/login', ['not_empty',['min_length',2],['max_length',32],'domain','unique'] )"
required
/>
<br/>
<div class="form-input-valid" ng-show="form.login.$pristine || (form.login.$dirty && rv.login.$valid)">
From 2 to 16 characters (numbers, letters and hyphens)
</div>
<span class="form-input-valid error" ng-show="form.login.$error.remoteValidate">
<span ng:bind="form.login.$message"></span>
</span>
BackEnd [Kohana]
public function action_validation(){
$field = $this->request->param('field');
$value = Arr::get($_POST,'value');
$rules = Arr::get($_POST,'rules',[]);
$aValid[$field] = $value;
$validation = Validation::factory($aValid);
foreach( $rules AS $rule ){
if( in_array($rule,['unique']) ){
/// Clients - Users Models
$validation = $validation->rule($field,$rule,[':field',':value','Clients']);
}
elseif( is_array($rule) ){ /// min_length, max_length
$validation = $validation->rule($field,$rule[0],[':value',$rule[1]]);
}
else{
$validation = $validation->rule($field,$rule);
}
}
$c = false;
try{
$c = $validation->check();
}
catch( Exception $e ){
$err = $e->getMessage();
Response::jEcho($err);
}
if( $c ){
$response = [
'isValid' => TRUE,
'message' => 'GOOD'
];
}
else{
$e = $validation->errors('validation');
$response = [
'isValid' => FALSE,
'message' => $e[$field]
];
}
Response::jEcho($response);
}