Validación dinámica de patrones ng de Angularjs

84

Tengo un formulario que, si una casilla de verificación es falsa, aplica la validación en una entrada de texto usando la directiva ng-required. Si la casilla de verificación es verdadera, el campo está oculto y ng-required se establece en falso.

El problema es que también tengo una expresión regular para la validación especificada en la entrada, además de utilizar la directiva angular ng-pattern. El problema con el que me estoy encontrando es que si un usuario completa un número de teléfono no válido, marca la casilla para desactivar esa entrada (y, en consecuencia, no necesita más validación), el formulario no permitirá el envío, ya que no es válido según el patrón ng.

Intenté resolver este problema agregando una función ng-change para establecer el modelo de entrada en nulo, sin embargo, el patrón ng y, por lo tanto, el campo todavía está configurado como no válido en el conjunto inicial de la casilla de verificación en falso. Sin embargo, si desmarco la casilla, estableciendo todo de nuevo en la carga inicial del formulario, luego vuelvo a marcar la casilla, el formulario es válido y se puede enviar. No estoy seguro de lo que me estoy perdiendo. Aquí está el código ng-change que tengo hasta ahora:

    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
fuente

Respuestas:

175

Este es un problema interesante, validación angular compleja. El siguiente violín implementa lo que quieres:

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

Detalles

Creé una nueva directiva, rpatternque es una combinación de Angular ng-requiredy el ng-patterncódigo de input[type=text]. Lo que hace es observar el requiredatributo del campo y tenerlo en cuenta al validar con regexp, es decir, si no es obligatorio marcar el campo como valid-pattern.

Notas

  • La mayor parte del código es de Angular, adaptado a las necesidades de este.
  • Cuando la casilla de verificación está marcada, el campo es obligatorio.
  • El campo no está oculto cuando la casilla de verificación requerida es falsa.
  • La expresión regular está simplificada para la demostración (la validez es de 3 dígitos).

Una solución sucia (pero más pequeña), si no desea una nueva directiva, sería 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);
        }
    };
})();

Y en HTML no se requerirían cambios:

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

En realidad, esto engaña a angular para que llame a nuestro test() método, en lugar de RegExp.test()que tenga requireden cuenta.

Nikos Paraskevopoulos
fuente
2
Me interesaría mucho saber qué hacen los corchetes que rodean y después de la función. No funciona sin ellos
NeedHack
5
Llaman a la función directamente, colocando efectivamente su valor de retorno a la variable. Es muy común en Javascript para no contaminar un alcance externo con detalles de implementación de un componente específico, por ejemplo, mirar el patrón del módulo .
Nikos Paraskevopoulos
6
La solución sucia, jugar un truco en AngularJS basado en "ducktyping" de JS es el código más asombroso que vi hoy. ¡Solución realmente inteligente!
Florian Loch
Gracias por el código increíble, pero aún no muy claro, 1) de acuerdo con docs.angularjs.org/api/ng/directive/input , "Si la expresión se evalúa como un objeto RegExp, entonces esto se usa directamente. Si la expresión evalúa a una cadena, luego se convertirá en una RegExp ", pero supongo que phoneNumberPattern es un objeto cuando regresa? ¿Se supone que esto es un obj Regxp? 2) Y en la función (valor) {} almacenar en prueba, ¿de dónde viene el parámetro "valor"? ¡Aprecio por las respuestas!
user2499325
1
1) La cláusula "Si la expresión se evalúa como RegExp" es implementada por Angular al verificar si lo que obtiene tiene un testmétodo. Es por eso que le damos un objeto con nuestra propia costumbre test(), emulando esencialmente un JS nativo RegExp. 2) Por valuesupuesto, Angular lo pasa al ejecutar la verificación de validez real.
Nikos Paraskevopoulos
25

Sin quitarle nada a la asombrosa respuesta de Nikos, tal vez pueda hacer esto de manera más simple:

<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 atención a la segunda entrada: podemos usar an ng-ifpara controlar la representación y la validación en formularios. Si la requireTelvariable no está configurada, la segunda entrada no solo se ocultará, sino que no se representará en absoluto, por lo que el formulario pasará la validación y el botón se habilitará, y obtendrá lo que necesita.

Kumarharsh
fuente
1
La ventaja es simple. La desventaja es que cualquier entrada que se elimine cuando la expresión ng-if es falsa tendrá el texto eliminado si ng-if se evalúa como verdadero más adelante. Aún así, +1 por simplicidad.
Brad
Quería agregar, si sigue esta ruta, asegúrese de comprender que todo dentro del ng-if (si lo coloca en un div, por ejemplo) ahora tendrá su propio alcance.
Brad
5

Patrón utilizado:

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

Archivo de referencia utilizado:

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

Ejemplo de entrada:

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

Me encontré con esto el otro día.

Lo que hice, que parece más fácil que lo anterior, es establecer el patrón en una variable en el alcance y referirme a él en ng-patrón en la vista.

Cuando "la casilla de verificación no está marcada", simplemente establezco el valor de expresión regular en /.*/ en la devolución de llamada onChanged (si no está marcada). ng-pattern toma ese cambio y dice "OK, tu valor está bien". El formulario ahora es válido. También eliminaría los datos incorrectos del campo para que no tenga un teléfono aparentemente defectuoso # sentado allí.

Tuve problemas adicionales relacionados con ng-required e hice lo mismo. Trabajado como un encanto.

jessewolfe
fuente
0

Establece la clave de error de validación del patrón si ngModel $ viewValue no coincide con una expresión regular encontrada al evaluar la expresión angular dada en el valor del atributo. Si la expresión se evalúa como un objeto RegExp, esto se usa directamente. Si la expresión se evalúa como una cadena, se convertirá en una expresión regular después de envolverla en caracteres ^ y $.

Parece que una respuesta más votada en esta pregunta debería actualizarse, porque cuando lo intento, no aplica la testfunción y la validación no funciona.

El ejemplo de Angular docs funciona bien para mí:

Modificación de 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

theodor
fuente
-1

Puede utilizar el sitio https://regex101.com/ para crear su propio patrón específico para algún país:

Por ejemplo, Polonia:

-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
fuente