Activar la validación de todos los campos en el envío de formulario angular

81

Estoy usando este método: http://plnkr.co/edit/A6gvyoXbBd2kfToPmiiA?p=preview para validar solo los campos en el desenfoque. Esto funciona bien, pero también me gustaría validarlos (y así mostrar los errores de esos campos, si los hubiera) cuando el usuario hace clic en el botón 'enviar' (no es un envío real sino una llamada de data-ng-click a una función)

¿Hay alguna forma de activar la validación en todos los campos nuevamente al hacer clic en ese botón?

Maarten
fuente
¿Dónde está el botón en el plunkr?
callmekatootie
lo siento, el plunker es en lo que basé mi código ... Hice una bifurcación para parecerse más a mi situación: plnkr.co/edit/VfvCSmjlzpIgUH4go2Jn?p=preview
Maarten

Respuestas:

44

Lo que funcionó para mí fue usar la $setSubmittedfunción, que aparece por primera vez en los documentos angulares en la versión 1.3.20.

En el evento de clic en el que quería activar la validación, hice lo siguiente:

vm.triggerSubmit = function() {
    vm.homeForm.$setSubmitted();
    ...
}

Eso fue todo lo que necesité para mí. Según los documentos, "establece el formulario en su estado enviado". Se menciona aquí .

revelador
fuente
4
esto no funciona cuando los está usando ng-messagesy solo los muestra cuando $ error && $ están sucios .
JobaDiniz
@JobaDiniz ¿Has probado la función $ setDirty? También se menciona en el enlace de mi respuesta: code.angularjs.org/1.3.20/docs/api/ng/type/form.FormController ¡ Espero que ayude!
programado el
2
no funciona para form... Tuve que recorrer todas las entradas y llamarlas $setDirty().
JobaDiniz
43

Lo sé, es un poco tarde para responder, pero todo lo que necesitas hacer es ensuciar todos los formularios. Eche un vistazo al siguiente fragmento:

angular.forEach($scope.myForm.$error.required, function(field) {
    field.$setDirty();
});

y luego puede verificar si su formulario es válido usando:

if($scope.myForm.$valid) {
    //Do something
}   

y finalmente, supongo, querrás cambiar tu ruta si todo se ve bien:

$location.path('/somePath');

Editar : el formulario no se registrará en el alcance hasta que se active el evento de envío. Simplemente use la directiva ng-submit para llamar a una función, y envuelva lo anterior en esa función, y debería funcionar.

Thilak Rao
fuente
1
¿Puede proporcionar un ejemplo de ng-submitdirectiva de activación programática ?
chovy
@chovy ng-submitsimplemente vincula una función al evento de envío, ¿por qué no llamar a esa función?
Thilak Rao
tengo una directiva incrustada en el formulario que actualiza un campo de formulario fuera de la directiva ... el validador no se está aplicando a menos que haga clic y desenfoque el campo de formulario que quiero validar.
Chovy
Me planteo' tengo el objeto de formulario que se pasa a ella
Chovy
@chovy No te entiendo bien. Pero déjame intentar ayudarte. ¿Ha intentado validar el rebote?
Thilak Rao
17

En caso de que alguien vuelva a esto más tarde ... Nada de lo anterior funcionó para mí. Así que indagué en las entrañas de la validación de formas angulares y encontré la función que llaman para ejecutar validadores en un campo determinado. Esta propiedad se llama convenientemente$validate .

Si tiene un formulario con nombre myForm, puede llamar mediante programación myForm.my_field.$validate()para ejecutar la validación de campo. Por ejemplo:

<div ng-form name="myForm">
    <input required name="my_field" type="text" ng-blur="myForm.my_field.$validate()">
</div>

Tenga en cuenta que la llamada $validatetiene implicaciones para su modelo. De los documentos angulares para ngModelCtrl. $ Validar:

Ejecuta cada uno de los validadores registrados (primero validadores síncronos y luego validadores asincrónicos). Si la validez cambia a inválida, el modelo se establecerá como indefinido, a menos que ngModelOptions.allowInvalid sea verdadero. Si la validez cambia a válida, establecerá el modelo en el último $ modelValue válido disponible, es decir, el último valor analizado o el último valor establecido del alcance.

Entonces, si planea hacer algo con el valor del modelo no válido (como mostrar un mensaje que se lo indique), debe asegurarse de que allowInvalidesté configurado truepara su modelo.

chukkwagon
fuente
más discusión sobre el resultado muy sorprendente "el modelo se vuelve indefinido" en ausencia de allowInvalidse puede leer aquí: github.com/angular/angular.js/issues/10035
pestophagous
12

Puede usar Angular-Validator para hacer lo que quiera. Es estúpido y simple de usar.

Va a:

  • Valide solo los campos en $dirtyo ensubmit
  • Impedir que se envíe el formulario si no es válido
  • Mostrar mensaje de error personalizado después de que se envíe el campo $dirtyo el formulario

Ver la demo

Ejemplo

<form angular-validator 
       angular-validator-submit="myFunction(myBeautifulForm)"
       name="myBeautifulForm">
       <!-- form fields here -->
    <button type="submit">Submit</button>
</form>

Si el campo no pasa el validator, el usuario no podrá enviar el formulario.

Consulte los casos de uso y ejemplos del validador angular para obtener más información.

Descargo de responsabilidad: soy el autor de Angular-Validator

user3920706
fuente
11

Bueno, la forma angular sería dejar que maneje la validación, ya que lo hace en cada cambio de modelo, y solo mostrar el resultado al usuario, cuando lo desee.

En este caso tú decides cuando mostrar los errores, solo tienes que poner una bandera: http://plnkr.co/edit/0NNCpQKhbLTYMZaxMQ9l?p=preview

Hasta donde yo sé, hay un problema archivado en angular para permitirnos tener un control de formulario más avanzado. Como no está resuelto, usaría esto en lugar de reinventar todos los métodos de validación existentes.

editar: Pero si insiste en su camino, aquí está su violín modificado con validación antes de enviar. http://plnkr.co/edit/Xfr7X6JXPhY9lFL3hnOw?p=preview El controlador transmite un evento cuando se hace clic en el botón, y la directiva hace la magia de validación.

Oliver
fuente
Esto funciona en este ejemplo, pero ¿qué pasa si yo, como tengo en mi caso (pero no en ese plunkr ... lo siento!), Más de una directiva como esa de correo electrónico. ¿De alguna manera tendría que mover la validación de las directivas a una clase de validación separada y luego llamar a todos los métodos de validación de este formulario, o puedo de alguna manera activar la validación de otra manera para todas las directivas? Como la validación se desencadena por desenfoque, tal vez incluso desencadenar desenfoques del código, pero eso parece horrible.
Maarten
Ah, y sé sobre el problema. Desafortunadamente, aún no está en versión beta y el flujo de trabajo del que estoy hablando es obligatorio para esta empresa
Maarten
El evento transmitido activará la devolución de llamada $ on en cada directiva, ya que todas están bajo el alcance del controlador.
Oliver
ahh ... realmente no entendí esa parte. ¡Gracias!
Maarten
1
Cuando difunde el evento, puede pasar parámetros. $ alcance. $ broadcast ('startValidations', param1, param2); La escucha permanece sin cambios: scope. $ On ('startValidations', validateMe); Y en la devolución de llamada fn: function validateMe (event, param1, param2) {} Consulte la documentación: docs.angularjs.org/api/ng.$rootScope.Scope#$broadcast
Oliver
9

Un enfoque es forzar a que todos los atributos estén sucios. Puedes hacer eso en cada controlador, pero se vuelve muy complicado. Sería mejor tener una solución general.

La forma más fácil que se me ocurrió fue utilizar una directiva

  • manejará el atributo de envío de formulario
  • itera a través de todos los campos de formulario y marca los campos prístinos como sucios
  • comprueba si el formulario es válido antes de llamar a la función de envío

Aquí está la directiva

myModule.directive('submit', function() {
  return {
    restrict: 'A',
    link: function(scope, formElement, attrs) {
      var form;
      form = scope[attrs.name];
      return formElement.bind('submit', function() {
        angular.forEach(form, function(field, name) {
          if (typeof name === 'string' && !name.match('^[\$]')) {
            if (field.$pristine) {
              return field.$setViewValue(field.$value);
            }
          }
        });
        if (form.$valid) {
          return scope.$apply(attrs.submit);
        }
      });
    }
  };
});

Y actualice su formulario html, por ejemplo:

 <form ng-submit='justDoIt()'>

se convierte en:

 <form name='myForm' novalidate submit='justDoIt()'>

Vea un ejemplo completo aquí: http://plunker.co/edit/QVbisEK2WEbORTAWL7Gu?p=preview

Joshnuss
fuente
4

Aquí está mi función global para mostrar los mensajes de error del formulario.

 function show_validation_erros(form_error_object) {
        angular.forEach(form_error_object, function (objArrayFields, errorName) {
            angular.forEach(objArrayFields, function (objArrayField, key) {
                objArrayField.$setDirty();
            });
        });
    };

Y en mis controladores,

if ($scope.form_add_sale.$invalid) { 
    $scope.global.show_validation_erros($scope.form_add_sale.$error);
}
Namal
fuente
1
Esto no responde a la pregunta.
Rafael Herscovici
Cambié mi respuesta. Compruébelo ahora
Namal
2

Basado en la respuesta de Thilak, pude encontrar esta solución ...

Dado que los campos de mi formulario solo muestran mensajes de validación si un campo no es válido y el usuario lo ha tocado, pude usar este código activado por un botón para mostrar mis campos no válidos:

// Show/trigger any validation errors for this step
angular.forEach(vm.rfiForm.stepTwo.$error, function(error) {
  angular.forEach(error, function(field) {
    field.$setTouched();
  });
});
// Prevent user from going to next step if current step is invalid
if (!vm.rfiForm.stepTwo.$valid) {
  isValid = false;
}
<!-- form field -->
<div class="form-group" ng-class="{ 'has-error': rfi.rfiForm.stepTwo.Parent_Suffix__c.$touched && rfi.rfiForm.stepTwo.Parent_Suffix__c.$invalid }">

  <!-- field label -->
  <label class="control-label">Suffix</label>
  <!-- end field label -->
  <!-- field input -->
  <select name="Parent_Suffix__c" class="form-control"
          ng-options="item.value as item.label for item in rfi.contact.Parent_Suffixes"
          ng-model="rfi.contact.Parent_Suffix__c" />
  <!-- end field input -->
  <!-- field help -->
  <span class="help-block" ng-messages="rfi.rfiForm.stepTwo.Parent_Suffix__c.$error" ng-show="rfi.rfiForm.stepTwo.Parent_Suffix__c.$touched">
    <span ng-message="required">this field is required</span>
  </span>  
  <!-- end field help -->
</div>
<!-- end form field -->

Charles Naccio
fuente
2

Nota: Sé que esto es un truco, pero fue útil para Angular 1.2 y versiones anteriores que no proporcionaban un mecanismo simple.

La validación se activa en el evento de cambio , por lo que algunas cosas como cambiar los valores mediante programación no lo activarán. Pero la activación del evento de cambio activará la validación. Por ejemplo, con jQuery:

$('#formField1, #formField2').trigger('change');
Jacob Mouka
fuente
Este enfoque es simple. Además, tiene la ventaja de que funciona en versiones anteriores (todas) de Angular.
Paul LeBeau
3
No el angular way.
Rafael Herscovici
1

Para validar todos los campos de mi formulario cuando quiero, hago una validación en cada campo de controles $$ como este:

angular.forEach($scope.myform.$$controls, function (field) {
    field.$validate();
});
Stephaneb
fuente
0

Me gusta este enfoque para manejar la validación al hacer clic en el botón.

  1. No es necesario invocar nada desde el controlador,

  2. todo se maneja con una directiva.

en github

Sathish Naga
fuente
0

Puedes probar esto:

// The controller

$scope.submitForm = function(form){
   		//Force the field validation
   		angular.forEach(form, function(obj){
   			if(angular.isObject(obj) && angular.isDefined(obj.$setDirty))
   			{ 
   				obj.$setDirty();
   			}
   		})
        
        if (form.$valid){
		
			$scope.myResource.$save(function(data){
		     	//....
			});
		}
}
<!-- FORM -->

  <form name="myForm"  role="form" novalidate="novalidate">
<!-- FORM GROUP to field 1 -->
  <div class="form-group" ng-class="{ 'has-error' : myForm.field1.$invalid && myForm.field1.$dirty }">
      <label for="field1">My field 1</label>
        <span class="nullable"> 
        <select name="field1" ng-model="myresource.field1" ng-options="list.id as list.name for list in listofall"
          class="form-control input-sm" required>
            <option value="">Select One</option>
        </select>
        </span>
        <div ng-if="myForm.field1.$dirty" ng-messages="myForm.field1.$error" ng-messages-include="mymessages"></div>
  </div>
    
<!-- FORM GROUP to field 2 -->
  <div class="form-group" ng-class="{ 'has-error' : myForm.field2.$invalid && myForm.field2.$dirty }">
    <label class="control-label labelsmall" for="field2">field2</label> 
      <input name="field2" min="1" placeholder="" ng-model="myresource.field2" type="number" 
      class="form-control input-sm" required>
    <div ng-if="myForm.field2.$dirty" ng-messages="myForm.field2.$error" ng-messages-include="mymessages"></div>
  </div>

  </form>

<!-- ... -->
<button type="submit" ng-click="submitForm(myForm)">Send</button>

Vinicius Trindade
fuente
0

Hice algo siguiente para que funcione.

<form name="form" name="plantRegistrationForm">
  <div ng-class="{ 'has-error': (form.$submitted || form.headerName.$touched) && form.headerName.$invalid }">
    <div class="col-md-3">
      <div class="label-color">HEADER NAME 
        <span class="red"><strong>*</strong></span></div>
    </div>
    <div class="col-md-9">
      <input type="text" name="headerName" id="headerName" 
             ng-model="header.headerName" 
             maxlength="100" 
             class="form-control" required>
      <div ng-show="form.$submitted || form.headerName.$touched">
        <span ng-show="form.headerName.$invalid" 
              class="label-color validation-message">Header Name is required</span>
      </div>
    </div>
  </div>

  <button ng-click="addHeader(form, header)" 
          type="button" 
          class="btn btn-default pull-right">Add Header
  </button>

</form>

En su controlador puede hacer;

addHeader(form, header){
        let self = this;
        form.$submitted = true;
        ... 
    }

También necesitas algo de CSS;

.label-color {
            color: $gray-color;
        }
.has-error {
       .label-color {
            color: rgb(221, 25, 29);
        }
        .select2-choice.ui-select-match.select2-default {
            border-color: #e84e40;
        }
    }
.validation-message {
       font-size: 0.875em;
    }
    .max-width {
        width: 100%;
        min-width: 100%;
    }
Mahib
fuente