AngularJS: integración con la validación del lado del servidor

Tengo una aplicación angular que contiene un botón guardar tomado de los ejemplos:

 

Esto funciona muy bien para la validación del lado del cliente porque el form.$invalid convierte en falso a medida que el usuario soluciona los problemas, pero tengo un campo de correo electrónico que se establece como no válido si otro usuario está registrado con el mismo correo electrónico.

Tan pronto como configure el campo de mi correo electrónico como no válido, no puedo enviar el formulario y el usuario no tiene forma de corregir ese error de validación. Así que ahora ya no puedo usar form.$invalid para deshabilitar mi botón de envío.

Debe haber una mejor manera

Lo necesitaba en algunos proyectos, así que creé una directiva. Finalmente se tomó un momento para ponerlo en GitHub para cualquiera que quiera una solución de instalación directa.

https://github.com/webadvanced/ng-remote-validate

caracteristicas:

  • Soltar la solución para la validación Ajax de cualquier entrada de texto o contraseña

  • Funciona con Angulars comstackción en validación y se puede acceder a tax en formName.inputName. $ Error.ngRemoteValidate

  • Solicitudes de servidor Throttles (400 ms predeterminados) y se pueden establecer con ng-remote-throttle="550"

  • Permite la definición del método HTTP (POST por defecto) con ng-remote-method="GET"

Ejemplo de uso para un formulario de cambio de contraseña que requiere que el usuario ingrese su contraseña actual así como la nueva contraseña .:

 

Change password

Required Incorrect current password. Please enter your current account password. New and confirm do not match

Este es otro caso donde una directiva personalizada es tu amiga. Deberá crear una directiva e inyectar $ http o $ resource en ella para hacer una callback al servidor mientras está validando.

Algunos pseudocódigos para la directiva personalizada:

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

Y así es como lo usarías en el margen:

  Email is not unique. 

EDITAR: una pequeña explicación de lo que está sucediendo arriba.

  1. Cuando actualiza el valor en la entrada, actualiza el $ scope.userEmail
  2. La directiva tiene un $ watch en $ scope.userEmail configurado en su función de enlace.
    • Cuando se activa el $ watch, realiza una llamada al servidor a través de la llamada $ http ajax, pasando el correo electrónico.
    • El servidor verificaría la dirección de correo electrónico y devolvería una respuesta simple como ‘{isValid: true}
    • esa respuesta se usa para $ setValidity del control.
  3. Hay un en el marcado con ng-show configurado para mostrar solo cuando el estado de validez de correo electrónico único es falso.

… para el usuario que significa:

  1. Escriba el correo electrónico.
  2. pausa leve.
  3. El mensaje “El correo electrónico no es único” muestra “tiempo real” si el correo electrónico no es único.

EDIT2: Esto también le permite usar form. $ Invalid para deshabilitar su botón de envío.

He creado Plunker con solución que funciona perfecto para mí. Utiliza directivas personalizadas pero en forma completa y no en un solo campo.

http://plnkr.co/edit/HnF90JOYaz47r8zaH5JY

No recomendaría deshabilitar el botón de enviar para la validación del servidor.

De acuerdo. En caso de que alguien necesite una versión de trabajo, está aquí:

Del 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.) 

Esto me hizo pensar que no necesitamos: $scope.$apply(function(s) { De lo contrario, se quejará de $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:

 
Name is not unique.

El controlador podría verse así:

 app.controller("UniqueFormController", function($scope) { $scope.name = "Bob" }) 

Gracias a las respuestas de esta página aprendimos sobre https://github.com/webadvanced/ng-remote-validate

Directivas de opciones, que es un poco menos de lo que realmente no me gusta, como cada campo para escribir la directiva. El módulo es el mismo: una solución universal.

Pero en los módulos me faltaba algo: revisa el campo para ver varias reglas.
Luego modifiqué el módulo https://github.com/borodatych/ngRemoteValidate
Disculpas por el LÉEME ruso, eventualmente se alterará.
Me apresuro a compartir de repente tener a alguien con el mismo problema.
Sí, y nos hemos reunido aquí para esto …

Carga:

  

Incluir:

 var app = angular.module( 'myApp', [ 'remoteValidate' ] ); 

HTML

  
From 2 to 16 characters (numbers, letters and hyphens)

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