[forms] Trigger validation of all fields in Angular Form submit

I'm using this method: http://plnkr.co/edit/A6gvyoXbBd2kfToPmiiA?p=preview to only validate fields on blur. This works fine, but I would also like to validate them (and thus show the errors for those fields if any) when the user clicks the 'submit' button (not a real submit but a data-ng-click call to a function)

Is there some way to trigger validation on all the fields again when clicking that button?

This question is related to forms angularjs

The answer is


Well, the angular way would be to let it handle validation, - since it does at every model change - and only show the result to the user, when you want.

In this case you decide when to show the errors, you just have to set a flag: http://plnkr.co/edit/0NNCpQKhbLTYMZaxMQ9l?p=preview

As far as I know there is a issue filed to angular to let us have more advanced form control. Since it is not solved i would use this instead of reinventing all the existing validation methods.

edit: But if you insist on your way, here is your modified fiddle with validation before submit. http://plnkr.co/edit/Xfr7X6JXPhY9lFL3hnOw?p=preview The controller broadcast an event when the button is clicked, and the directive does the validation magic.


In case someone comes back to this later... None of the above worked for me. So I dug down into the guts of angular form validation and found the function they call to execute validators on a given field. This property is conveniently called $validate.

If you have a named form myForm, you can programmatically call myForm.my_field.$validate() to execute field validation. For example:

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

Note that calling $validate has implications for your model. From the angular docs for ngModelCtrl.$validate:

Runs each of the registered validators (first synchronous validators and then asynchronous validators). If the validity changes to invalid, the model will be set to undefined, unless ngModelOptions.allowInvalid is true. If the validity changes to valid, it will set the model to the last available valid $modelValue, i.e. either the last parsed value or the last value set from the scope.

So if you're planning on doing something with the invalid model value (like popping a message telling them so), then you need to make sure allowInvalid is set to true for your model.


I know, it's a tad bit too late to answer, but all you need to do is, force all forms dirty. Take a look at the following snippet:

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

and then you can check if your form is valid using:

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

and finally, I guess, you would want to change your route if everything looks good:

$location.path('/somePath');

Edit: form won't register itself on the scope until submit event is trigger. Just use ng-submit directive to call a function, and wrap the above in that function, and it should work.


I like the this approach in handling validation on button click.

  1. There is no need to invoke anything from controller,

  2. it's all handled with a directive.

on github


One approach is to force all attributes to be dirty. You can do that in each controller, but it gets very messy. It would be better to have a general solution.

The easiest way I could think of was to use a directive

  • it will handle the form submit attribute
  • it iterates through all form fields and marks pristine fields dirty
  • it checks if the form is valid before calling the submit function

Here is the directive

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

And update your form html, for example:

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

becomes:

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

See a full example here: http://plunker.co/edit/QVbisEK2WEbORTAWL7Gu?p=preview


You can try this:

_x000D_
_x000D_
// The controller_x000D_
_x000D_
$scope.submitForm = function(form){_x000D_
     //Force the field validation_x000D_
     angular.forEach(form, function(obj){_x000D_
      if(angular.isObject(obj) && angular.isDefined(obj.$setDirty))_x000D_
      { _x000D_
       obj.$setDirty();_x000D_
      }_x000D_
     })_x000D_
        _x000D_
        if (form.$valid){_x000D_
  _x000D_
   $scope.myResource.$save(function(data){_x000D_
        //...._x000D_
   });_x000D_
  }_x000D_
}
_x000D_
<!-- FORM -->_x000D_
_x000D_
  <form name="myForm"  role="form" novalidate="novalidate">_x000D_
<!-- FORM GROUP to field 1 -->_x000D_
  <div class="form-group" ng-class="{ 'has-error' : myForm.field1.$invalid && myForm.field1.$dirty }">_x000D_
      <label for="field1">My field 1</label>_x000D_
        <span class="nullable"> _x000D_
        <select name="field1" ng-model="myresource.field1" ng-options="list.id as list.name for list in listofall"_x000D_
          class="form-control input-sm" required>_x000D_
            <option value="">Select One</option>_x000D_
        </select>_x000D_
        </span>_x000D_
        <div ng-if="myForm.field1.$dirty" ng-messages="myForm.field1.$error" ng-messages-include="mymessages"></div>_x000D_
  </div>_x000D_
    _x000D_
<!-- FORM GROUP to field 2 -->_x000D_
  <div class="form-group" ng-class="{ 'has-error' : myForm.field2.$invalid && myForm.field2.$dirty }">_x000D_
    <label class="control-label labelsmall" for="field2">field2</label> _x000D_
      <input name="field2" min="1" placeholder="" ng-model="myresource.field2" type="number" _x000D_
      class="form-control input-sm" required>_x000D_
    <div ng-if="myForm.field2.$dirty" ng-messages="myForm.field2.$error" ng-messages-include="mymessages"></div>_x000D_
  </div>_x000D_
_x000D_
  </form>_x000D_
_x000D_
<!-- ... -->_x000D_
<button type="submit" ng-click="submitForm(myForm)">Send</button>
_x000D_
_x000D_
_x000D_


I done something following to make it work.

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

In your controller you can do;

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

You need some css as well;

.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%;
    }

Note: I know this is a hack, but it was useful for Angular 1.2 and earlier that didn't provide a simple mechanism.

The validation kicks in on the change event, so some things like changing the values programmatically won't trigger it. But triggering the change event will trigger the validation. For example, with jQuery:

$('#formField1, #formField2').trigger('change');

Based on Thilak's answer I was able to come up with this solution...

Since my form fields only show validation messages if a field is invalid, and has been touched by the user I was able to use this code triggered by a button to show my invalid fields:

_x000D_
_x000D_
// Show/trigger any validation errors for this step_x000D_
angular.forEach(vm.rfiForm.stepTwo.$error, function(error) {_x000D_
  angular.forEach(error, function(field) {_x000D_
    field.$setTouched();_x000D_
  });_x000D_
});_x000D_
// Prevent user from going to next step if current step is invalid_x000D_
if (!vm.rfiForm.stepTwo.$valid) {_x000D_
  isValid = false;_x000D_
}
_x000D_
<!-- form field -->_x000D_
<div class="form-group" ng-class="{ 'has-error': rfi.rfiForm.stepTwo.Parent_Suffix__c.$touched && rfi.rfiForm.stepTwo.Parent_Suffix__c.$invalid }">_x000D_
_x000D_
  <!-- field label -->_x000D_
  <label class="control-label">Suffix</label>_x000D_
  <!-- end field label -->_x000D_
  <!-- field input -->_x000D_
  <select name="Parent_Suffix__c" class="form-control"_x000D_
          ng-options="item.value as item.label for item in rfi.contact.Parent_Suffixes"_x000D_
          ng-model="rfi.contact.Parent_Suffix__c" />_x000D_
  <!-- end field input -->_x000D_
  <!-- field help -->_x000D_
  <span class="help-block" ng-messages="rfi.rfiForm.stepTwo.Parent_Suffix__c.$error" ng-show="rfi.rfiForm.stepTwo.Parent_Suffix__c.$touched">_x000D_
    <span ng-message="required">this field is required</span>_x000D_
  </span>  _x000D_
  <!-- end field help -->_x000D_
</div>_x000D_
<!-- end form field -->
_x000D_
_x000D_
_x000D_


You can use Angular-Validator to do what you want. It's stupid simple to use.

It will:

  • Only validate the fields on $dirty or on submit
  • Prevent the form from being submitted if it is invalid
  • Show custom error message after the field is $dirty or the form is submitted

See the demo

Example

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

If the field does not pass the validator then the user will not be able to submit the form.

Check out angular-validator use cases and examples for more information.

Disclaimer: I am the author of Angular-Validator


To validate all fields of my form when I want, I do a validation on each field of $$controls like this :

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

Here is my global function for showing the form error messages.

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

And in my any controllers,

if ($scope.form_add_sale.$invalid) { 
    $scope.global.show_validation_erros($scope.form_add_sale.$error);
}