Validar campos depois que o usuário deixou um campo

Com o AngularJS, posso usar ng-pristine ou ng-dirty para detectar se o usuário entrou no campo. No entanto, eu quero fazer a validação do lado do cliente somente depois que o usuário deixou a área de campo. Isso ocorre porque, quando um usuário digita um campo como e-mail ou telefone, ele sempre recebe um erro até que tenha concluído a digitação do e-mail completo, e essa não é uma experiência de usuário ideal.

Exemplo


ATUALIZAR:

O Angular agora vem com um evento de desfoque personalizado: https://docs.angularjs.org/api/ng/directive/ngBlur

Angular 1.3 agora tem ng-model-options, e você pode definir a opção para { 'updateOn': 'blur'} por exemplo, e você pode até mesmo debounce, quando o uso está digitando muito rápido, ou você quer salvar um Poucas operações DOM dispendiosas (como um modelo escrevendo para vários lugares DOM e você não quer que um ciclo $ digest esteja acontecendo em cada tecla)

http://www.google.com/support

Por padrão, qualquer alteração no conteúdo acionará uma atualização de modelo e validação de formulário. Você pode replace esse comportamento usando a diretiva ngModelOptions para vincular somente a lista especificada de events. Ou seja, ng-model-options = “{updateOn: ‘blur’}” irá atualizar e validar somente depois que o controle perder o foco. Você pode definir vários events usando uma lista delimitada por espaço. Ou seja, ng-model-options = “{updateOn: ‘borrão de mousedown’}”

E debounce

Você pode atrasar a atualização / validação do modelo usando a chave debounce com a diretiva ngModelOptions. Esse atraso também será aplicado a analisadores, validadores e flags de modelos como $ dirty ou $ pristine.

Ou seja, ng-model-options = “{debounce: 500}” vai esperar por meio segundo desde a última alteração do conteúdo antes de acionar a atualização do modelo e a validação do formulário.

A partir da versão 1.3.0 isso pode ser feito facilmente com $touched , o que é verdade depois que o usuário deixou o campo.

 

Error

https://docs.angularjs.org/api/ng/type/ngModel.NgModelController

Eu resolvi isso expandindo o que @jlmcdonald sugeriu. Eu criei uma diretiva que seria aplicada automaticamente a todos os elementos de input e seleção:

 var blurFocusDirective = function () { return { restrict: 'E', require: '?ngModel', link: function (scope, elm, attr, ctrl) { if (!ctrl) { return; } elm.on('focus', function () { elm.addClass('has-focus'); scope.$apply(function () { ctrl.hasFocus = true; }); }); elm.on('blur', function () { elm.removeClass('has-focus'); elm.addClass('has-visited'); scope.$apply(function () { ctrl.hasFocus = false; ctrl.hasVisited = true; }); }); elm.closest('form').on('submit', function () { elm.addClass('has-visited'); scope.$apply(function () { ctrl.hasFocus = false; ctrl.hasVisited = true; }); }); } }; }; app.directive('input', blurFocusDirective); app.directive('select', blurFocusDirective); 

Isso adicionará classs de has-focus e has-visited a vários elementos à medida que o usuário se concentrar / visitar os elementos. Você pode adicionar essas classs às suas regras CSS para mostrar erros de validação:

 input.has-visited.ng-invalid:not(.has-focus) { background-color: #ffeeee; } 

Isso funciona bem em elementos que ainda recebem propriedades inválidas $ etc, mas o CSS pode ser usado para dar ao usuário uma melhor experiência.

Eu consegui fazer isso com um pouco simples de CSS. Isso exige que as mensagens de erro sejam irmãos da input a que se referem e que tenham uma class de error .

 :focus ~ .error { display:none; } 

Depois de atender a esses dois requisitos, isso ocultará qualquer mensagem de erro relacionada a um campo de input focado, algo que acho que o angularjs deveria estar fazendo de qualquer maneira. Parece um descuido.

Isso parece ser implementado como padrão nas versões mais recentes do angular.

As classs ng-untouched e ng-touch são definidas respectivamente antes e depois do usuário ter tido foco em um elemento validado.

CSS

 input.ng-touched.ng-invalid { border-color: red; } 

Em relação à solução do @ lambinator … eu estava recebendo o seguinte erro no angular.js 1.2.4:

Erro: [$ rootScope: inprog] $ digest já em andamento

Não tenho certeza se fiz algo errado ou se isso é uma alteração no Angular, mas removendo o scope.$apply statements resolveu o problema e as classs / estados ainda estão sendo atualizadas.

Se você também estiver vendo esse erro, tente o seguinte:

 var blurFocusDirective = function () { return { restrict: 'E', require: '?ngModel', link: function (scope, elm, attr, ctrl) { if (!ctrl) { return; } elm.on('focus', function () { elm.addClass('has-focus'); ctrl.$hasFocus = true; }); elm.on('blur', function () { elm.removeClass('has-focus'); elm.addClass('has-visited'); ctrl.$hasFocus = false; ctrl.$hasVisited = true; }); elm.closest('form').on('submit', function () { elm.addClass('has-visited'); scope.$apply(function () { ctrl.hasFocus = false; ctrl.hasVisited = true; }); }); } }; }; app.directive('input', blurFocusDirective); app.directive('select', blurFocusDirective); 

Pode funcionar para você escrever uma diretiva personalizada que envolva o método javascript blur () (e execute uma function de validação quando acionada); há um problema angular que tem um exemplo (assim como uma diretiva genérica que pode se ligar a outros events não suportados nativamente pelo Angular):

https://github.com/angular/angular.js/issues/1277

Se você não quiser seguir esse caminho, sua outra opção seria configurar $ watch no campo, novamente acionando a validação quando o campo for preenchido.

Para saber mais sobre as respostas dadas, você pode simplificar a marcação de input usando pseudo-classs CSS3 e marcando apenas campos visitados com uma class para atrasar a exibição de erros de validação até que o usuário perca o foco no campo:

(Exemplo requer jQuery)

JavaScript

 module = angular.module('app.directives', []); module.directive('lateValidateForm', function () { return { restrict: 'AC', link: function (scope, element, attrs) { $inputs = element.find('input, select, textarea'); $inputs.on('blur', function () { $(this).addClass('has-visited'); }); element.on('submit', function () { $inputs.addClass('has-visited'); }); } }; }); 

CSS

 input.has-visited:not(:focus):required:invalid, textarea.has-visited:not(:focus):required:invalid, select.has-visited:not(:focus):required:invalid { color: #b94a48; border-color: #ee5f5b; } 

HTML

 

baseado em @nicolas resposta .. Pure CSS deve o truque, ele só irá mostrar a mensagem de erro no borrão

  

This is not a valid email.

CSS

 .ng-invalid:focus ~ .bg-danger { display:none; } 

Aqui está um exemplo usando ng-mensagens (disponíveis em 1.3) e uma diretiva personalizada.

A mensagem de validação é exibida no desfoque durante o primeiro tempo que o usuário deixa o campo de input, mas quando ele corrige o valor, a mensagem de validação é removida imediatamente (não mais no borrão).

JavaScript

 myApp.directive("validateOnBlur", [function() { var ddo = { restrict: "A", require: "ngModel", scope: {}, link: function(scope, element, attrs, modelCtrl) { element.on('blur', function () { modelCtrl.$showValidationMessage = modelCtrl.$dirty; scope.$apply(); }); } }; return ddo; }]); 

HTML

 
name is required name is too short name is too long

PS. Não esqueça de baixar e include ngMessages no seu módulo:

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

ng-model-options em AngularJS 1.3 (beta até o momento desta publicação) está documentado para suportar {updateOn: ‘blur’}. Para versões anteriores, algo como o seguinte funcionou para mim:

 myApp.directive('myForm', function() { return { require: 'form', link: function(scope, element, attrs, formController) { scope.validate = function(name) { formController[name].isInvalid = formController[name].$invalid; }; } }; }); 

Com um modelo como este:

 
Please enter a valid e-mail address.

Use field state $ touched O campo foi tocado para isso, como mostrado no exemplo abaixo.

 
You must enter a value

Você pode definir dinamicamente a class css has-error (assumindo que você está usando bootstrap) usando ng-class e uma propriedade no escopo do controller associado:

plunkr: http://plnkr.co/edit/HYDlaTNThZE02VqXrUCH?p=info

HTML:

 

Controlador:

 $scope.badEmailAddress = false; $scope.emailBlurred = function (isValid) { $scope.badEmailAddress = !isValid; }; 

Se você usar o bootstrap 3 e o lesscss, poderá ativar a validação de desfoque com o seguinte fragment a menos:

 :focus ~ .form-control-feedback.glyphicon-ok { display:none; } :focus ~ .form-control-feedback.glyphicon-remove { display:none; } .has-feedback > :focus { & { .form-control-focus(); } } 

outI usou uma diretiva. Aqui está o código:

 app.directive('onBlurVal', function () { return { restrict: 'A', link: function (scope, element, attrs, controller) { element.on('focus', function () { element.next().removeClass('has-visited'); element.next().addClass('has-focus'); }); element.on('blur', function () { element.next().removeClass('has-focus'); element.next().addClass('has-visited'); }); } } }) 

Todo o meu controle de input tem um elemento span como o próximo elemento, que é onde minha mensagem de validação é exibida e, portanto, a diretiva como um atributo é adicionada a cada controle de input.

Eu também tenho (opcional) .has-foco e visitou a class css no meu arquivo css que você vê sendo referenciado na diretiva.

NOTA: lembre-se de adicionar ‘on-blur-val’ exatamente desta forma ao seu controle de input sem os apóstrofos

Usando o ng-focus, você pode alcançar seu objective. você precisa fornecer ng-focus em seu campo de input. E ao escrever seus derivados ng-show você tem que escrever uma lógica não igual também. Como o código abaixo:

Podemos usar as funções onfocus e onblur. Seria simples e melhor.

  
Name:
E-mail:

Tente aqui:

http://plnkr.co/edit/NKCmyru3knQiShFZ96tp?p=preview