Validação de padrão ng dinâmico de Angularjs

84

Eu tenho um formulário que, se uma caixa de seleção for falsa, impõe a validação em uma entrada de texto usando a diretiva ng-required. Se a caixa de seleção for verdadeira, o campo ficará oculto e o ng-obrigatório será definido como falso.

O problema é que eu também tenho uma regex para validação especificada na entrada, também utilizando a diretiva angular do padrão ng. O problema que estou encontrando é que se um usuário preencher um número de telefone inválido, marcar a caixa para desativar essa entrada (e, consequentemente, não precisar de mais validação), o formulário não permitirá o envio, pois é inválido com base no padrão ng.

Tentei resolver esse problema adicionando uma função de mudança de ng para definir o modelo de entrada como nulo, no entanto, o padrão de ng e, portanto, o campo ainda está definido como inválido no conjunto inicial da caixa de seleção como falso. No entanto, se eu desmarcar a caixa, configurando tudo de volta para o carregamento inicial do formulário, marque a caixa novamente, o formulário é válido e pode ser enviado. Não tenho certeza do que estou perdendo. Aqui está o código ng-change que tenho até agora:

    var phoneNumberRegex = /^\(?(\d{3})\)?[ .-]?(\d{3})[ .-]?(\d{4})$/;
    $scope.phoneNumberPattern = phoneNumberRegex;
    $scope.removeValidation = function() {
        if ($scope.cell._newUser === false) {
            $scope.request._number = '';
            $scope.phoneNumberPattern = /[0-9a-zA-Z]?/;
        } else {
            $scope.phoneNumberPattern = phoneNumberRegex;
        }
    };
Brian
fonte

Respostas:

175

Este é um problema interessante, validação angular complexa. O seguinte violino implementa o que você deseja:

http://jsfiddle.net/2G8gA/1/

Detalhes

Criei uma nova diretiva, rpatternque é uma mistura do Angular ng-requirede do ng-patterncódigo de input[type=text]. O que ele faz é observar o requiredatributo do campo e levar isso em consideração ao validar com regexp, ou seja, se não for obrigatório, marque o campo como valid-pattern.

Notas

  • A maior parte do código é do Angular, adaptado às necessidades deste.
  • Quando a caixa de seleção está marcada, o campo é obrigatório.
  • O campo não fica oculto quando a caixa de seleção obrigatória é falsa.
  • A expressão regular é simplificada para a demonstração (a válida é 3 dígitos).

Uma solução suja (mas menor), se você não quiser uma nova diretiva, seria algo como:

$scope.phoneNumberPattern = (function() {
    var regexp = /^\(?(\d{3})\)?[ .-]?(\d{3})[ .-]?(\d{4})$/;
    return {
        test: function(value) {
            if( $scope.requireTel === false ) {
                return true;
            }
            return regexp.test(value);
        }
    };
})();

E em HTML nenhuma alteração seria necessária:

<input type="text" ng-model="..." ng-required="requireTel"
    ng-pattern="phoneNumberPattern" />

Isso realmente engana o angular para chamar nosso test() método, em vez de RegExp.test()levar o requiredem consideração.

Nikos Paraskevopoulos
fonte
2
Eu estaria muito interessado em saber o que os colchetes ao redor e após a função fazem. Não funciona sem eles
NeedHack
5
Eles chamam a função diretamente, colocando efetivamente seu valor de retorno na variável. É muito comum em Javascript não poluir um escopo externo com detalhes de implementação de um componente específico, por exemplo, observe o padrão do módulo .
Nikos Paraskevopoulos
6
A solução suja, pregar uma peça no AngularJS baseado no "ducktyping" do JS, é o código mais incrível que vi hoje. Solução realmente inteligente!
Florian Loch
Obrigado pelo código incrível, mas ainda não muito claro, 1) de acordo com docs.angularjs.org/api/ng/directive/input , "Se a expressão for avaliada como um objeto RegExp, ele será usado diretamente. Se a expressão for avaliada em uma string, ela será convertida em RegExp ", mas suponho que phoneNumberPattern seja um objeto quando retornar? Isso deveria ser um Regxp Obj? 2) E na função (valor) {} loja em teste, de onde vem o parâmetro "valor"? Agradeço todas as respostas!
user2499325
1
1) A cláusula "Se a expressão for avaliada como RegExp" é implementada pelo Angular verificando se o objeto obtido possui um testmétodo. É por isso que damos a ele um objeto com nosso próprio costume test(), essencialmente emulando um JS nativo RegExp. 2) valueÉ claro que o Angular é aprovado ao executar a verificação de validade real.
Nikos Paraskevopoulos
25

Sem tirar nada da incrível resposta de Nikos, talvez você possa fazer isso de forma mais simples:

<form name="telForm">
  <input name="cb" type='checkbox' data-ng-modal='requireTel'>
  <input name="tel" type="text" ng-model="..." ng-if='requireTel' ng-pattern="phoneNumberPattern" required/>
  <button type="submit" ng-disabled="telForm.$invalid || telForm.$pristine">Submit</button>
</form>

Preste atenção à segunda entrada: Podemos usar um ng-ifpara controlar a renderização e validação em formulários. Se a requireTelvariável não estiver definida, a segunda entrada não apenas será ocultada, mas não será renderizada, portanto, o formulário passará na validação e o botão será ativado, e você obterá o que precisa.

Kumarharsh
fonte
1
O lado bom é simples. A desvantagem é que qualquer entrada que for removida quando a expressão ng-if for falsa terá qualquer texto removido se ng-if for avaliado como verdadeiro posteriormente. Ainda assim, 1 para simplicidade.
Brad
Eu queria adicionar, se você seguir esse caminho, certifique-se de entender que tudo dentro do ng-if (se você colocá-lo em um div, por exemplo) agora terá seu próprio escopo.
Brad
5

Padrão usado:

 ng-pattern="/^\d{0,9}(\.\d{1,9})?$/"

Arquivo de referência usado:

 '<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.6/angular.js"></script>'

Exemplo de entrada:

 <input type="number" require ng-pattern="/^\d{0,9}(\.\d{1,9})?$/"><input type="submit">
Anil Singh
fonte
2

 <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.6/angular.js"></script>
<input type="number" require ng-pattern="/^\d{0,9}(\.\d{1,9})?$/"><input type="submit">

Jijo Paulose
fonte
2

Eu acabei de encontrar isso outro dia.

O que eu fiz, o que parece mais fácil do que o anterior, foi definir o padrão em uma variável no escopo e fazer referência a ele no padrão ng na visualização.

Quando "a caixa de seleção está desmarcada", simplesmente defino o valor regex para /.*/ no retorno de chamada onChanged (se for desmarcado). O ng-pattern pega essa mudança e diz "OK, seu valor está bom". O formulário agora é válido. Eu também removeria os dados ruins do campo, para que você não tenha um telefone aparentemente ruim # sentado lá.

Tive problemas adicionais em torno do ng-required e fiz a mesma coisa. Funcionou como um encanto.

Jessewolfe
fonte
0

Define a chave de erro de validação de padrão se o ngModel $ viewValue não corresponder a um RegExp encontrado avaliando a expressão Angular fornecida no valor do atributo. Se a expressão for avaliada como um objeto RegExp, ele será usado diretamente. Se a expressão for avaliada como uma string, ela será convertida em RegExp após envolvê-la nos caracteres ^ e $.

Parece que uma resposta mais votada nesta questão deve ser atualizada, porque quando tento fazer isso, não se aplica a testfunção e a validação não funciona.

O exemplo da documentação Angular funciona bem para mim:

Modificando validadores integrados

html

<form name="form" class="css-form" novalidate>
  <div>
   Overwritten Email:
   <input type="email" ng-model="myEmail" overwrite-email name="overwrittenEmail" />
   <span ng-show="form.overwrittenEmail.$error.email">This email format is invalid!</span><br>
   Model: {{myEmail}}
  </div>
</form>

js

var app = angular.module('form-example-modify-validators', []);

app.directive('overwriteEmail', function() {
    var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+/=?^_`{|}~.-]+@example\.com$/i;

    return {
        require: 'ngModel',
        restrict: '',
        link: function(scope, elm, attrs, ctrl) {
            // only apply the validator if ngModel is present and Angular has added the email validator
            if (ctrl && ctrl.$validators.email) {

                // this will overwrite the default Angular email validator
                ctrl.$validators.email = function(modelValue) {
                    return ctrl.$isEmpty(modelValue) || EMAIL_REGEXP.test(modelValue);
                };
             }
         }
     };
 });

Plunker

teodor
fonte
-1

Você pode usar o site https://regex101.com/ para construir seu próprio padrão específico para algum país:

Por exemplo, Polônia:

-pattern = xxxxxxxxx OR xxx-xxx-xxx OR xxx xxx xxx 
-regexp ="^\d{9}|^\d{3}-\d{3}-\d{3}|^\d{3}\s\d{3}\s\d{3}"
Lepton
fonte