AngularJS: integrando com validação do lado do servidor

Eu tenho um aplicativo angular que contém um botão salvar retirado dos exemplos:

 

Isso funciona muito bem para a validação do lado do cliente porque o form.$invalid se torna falso, pois o usuário corrige problemas, mas eu tenho um campo de email que é definido como inválido se outro usuário estiver registrado com o mesmo email.

Assim que eu definir meu campo de e-mail como inválido, não posso enviar o formulário, e o usuário não tem como corrigir esse erro de validação. Então agora eu não posso mais usar o form.$invalid para desativar meu botão de envio.

Deve haver uma maneira melhor

Eu precisava disso em alguns projetos, então criei uma diretiva. Finalmente, demorou um momento para colocá-lo no GitHub para quem quiser uma solução de drop-in.

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

Características:

  • Queda na solução para validação Ajax de qualquer input de texto ou senha

  • O Works with Angulars é compilado na validação e o cab pode ser acessado em formName.inputName. $ Error.ngRemoteValidate

  • Aciona as solicitações do servidor (padrão 400 ms) e pode ser definido com ng-remote-throttle="550"

  • Permite a definição do método HTTP (padrão POST) com ng-remote-method="GET"

Exemplo de uso para um formulário de alteração de senha que exige que o usuário insira a senha atual e a nova senha:

 

Change password

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

Este é outro caso em que uma diretiva personalizada é sua amiga. Você desejará criar uma diretiva e injetar $ http ou $ resource nela para fazer uma chamada de volta ao servidor enquanto estiver validando.

Algum pseudocódigo para a diretiva 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); }) } } }); 

E aqui está como você usaria na marcação:

  Email is not unique. 

EDIT: uma pequena explicação do que está acontecendo acima.

  1. Quando você atualiza o valor na input, ele atualiza o $ scope.userEmail
  2. A diretiva tem um $ watch em $ scope.userEmail configurado em sua function de vinculação.
    • Quando o $ watch é acionado, ele faz uma chamada para o servidor via $ ajax http, passando o email
    • O servidor verificaria o endereço de e-mail e retornaria uma resposta simples como ‘{isValid: true}
    • essa resposta é usada para $ setValidity do controle.
  3. Existe um na marcação com ng-show configurado para mostrar apenas quando o estado de validade uniqueEmail é falso.

… para o usuário, isso significa:

  1. Digite o email.
  2. pequena pausa.
  3. A mensagem “E-mail não é única” exibe “tempo real” se o e-mail não for exclusivo.

EDIT2: Isto também permite que você use o formulário. $ Invalid para desativar seu botão de envio.

Eu criei plunker com solução que funciona perfeito para mim. Ele usa diretiva personalizada, mas em todo o formulário e não em um único campo.

http://plnkr.co/edit/HnF90JOYaz47r8zaH5JY

Eu não recomendaria desabilitar o botão de envio para validação do servidor.

Está bem. No caso, se alguém precisa de versão de trabalho, é aqui:

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

Isso me fez pensar que não precisamos de: $scope.$apply(function(s) { senão ele irá reclamar sobre $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.

Controlador pode ser assim:

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

Graças às respostas desta página, você aprendeu sobre https://github.com/webadvanced/ng-remote-validate

Diretivas de opção, que é um pouco menos do que eu realmente não gostei, como cada campo para escrever a diretiva. Módulo é o mesmo – uma solução universal.

Mas nos módulos eu estava faltando alguma coisa – verifique o campo para várias regras.
Então eu apenas modifiquei o módulo https://github.com/borodatych/ngRemoteValidate
Desculpas pelo README russo, eventualmente serão alteradas.
Eu apresso a compartilhar de repente ter alguém com o mesmo problema.
Sim, e nós nos reunimos aqui para isso …

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