[javascript] show validation error messages on submit in angularjs

I have a form which need to show validation error messages if clicked submit.

Here is a working plunker

 <form name="frmRegister" ng-submit="register();" novalidate>
      <div>
        <input placeholder="First Name" name="first_name" type="text" ng-model="user.firstName" required />
        <span ng-show="frmRegister.first_name.$dirty && frmRegister.first_name.$error.required">First Name is required</span>
      </div>
      <div>
        <input placeholder="Last Name" name="last_name" type="text" ng-model="user.lastName" required />
        <span ng-show="frmRegister.last_name.$dirty && frmRegister.last_name.$error.required">Last Name is required</span>
      </div>
      <div>
        <input placeholder="Email" name="email" type="email" ng-model="user.email" required />
        <span ng-show="frmRegister.email.$dirty && frmRegister.email.$error.required">Email is required.</span>
        <span ng-show="frmRegister.email.$dirty && frmRegister.email.$error.email">Invalid Email address.</span>
      </div>
      <input type="submit" value="Save" />
      <span ng-show="registered">YOU ARE NOW REGISTERED USER</span>
 </form>

Validation works normally when user start making changes. But it doesn't show any error messages If clicked submit without entering anything.

Any thought of achieving this?. Or in other way how can I make each input field $dirty when clicks the Submit button

This question is related to javascript validation angularjs

The answer is


I like the solution from realcrowd the best.

HTML:

<form role="form" id="form" name="form" autocomplete="off" novalidate rc-submit="signup()">
<div class="form-group" ng-class="{'has-error':  rc.form.hasError(form.firstName)}">
    <label for="firstName">Your First Name</label>
    <input type="text" id="firstName" name="firstName" class="form-control input-sm" placeholder="First Name" ng-maxlength="40" required="required" ng-model="owner.name.first"/>
    <div class="help-block" ng-show="rc.form.hasError(form.firstName)">{{rc.form.getErrMsg(form.firstName)}}</div>
</div>
</form>

javascript:

//define custom submit directive
var rcSubmitDirective = {
'rcSubmit': ['$parse', function ($parse) {
    return {
        restrict: 'A',
        require: ['rcSubmit', '?form'],
        controller: ['$scope', function ($scope) {
            this.attempted = false;

            var formController = null;

            this.setAttempted = function() {
                this.attempted = true;
            };

            this.setFormController = function(controller) {
              formController = controller;
            };

            this.hasError = function (fieldModelController) {
                if (!formController) return false;

                if (fieldModelController) {
                    return fieldModelController.$invalid && this.attempted;
                } else {
                    return formController && formController.$invalid && this.attempted;
                }
            };
            this.getErrMsg=function(ctrl){
                var e=ctrl.$error;
                var errMsg;
                if (e.required){
                    errMsg='Please enter a value';
                }
                return errMsg;
            }
        }],
        compile: function(cElement, cAttributes, transclude) {
            return {
                pre: function(scope, formElement, attributes, controllers) {

                    var submitController = controllers[0];
                    var formController = (controllers.length > 1) ? controllers[1] : null;

                    submitController.setFormController(formController);

                    scope.rc = scope.rc || {};
                    scope.rc[attributes.name] = submitController;
                },
                post: function(scope, formElement, attributes, controllers) {

                    var submitController = controllers[0];
                    var formController = (controllers.length > 1) ? controllers[1] : null;
                    var fn = $parse(attributes.rcSubmit);

                    formElement.bind('submit', function (event) {
                        submitController.setAttempted();
                        if (!scope.$$phase) scope.$apply();

                        if (!formController.$valid) return false;

                        scope.$apply(function() {
                            fn(scope, {$event:event});
                        });
                    });
                }
          };
        }
    };
}]
};
app.directive(rcSubmitDirective);

Since I'm using Bootstrap 3, I use a directive: (see plunkr)

    var ValidSubmit = ['$parse', function ($parse) {
        return {
            compile: function compile(tElement, tAttrs, transclude) {
                return {
                    post: function postLink(scope, element, iAttrs, controller) {
                        var form = element.controller('form');
                        form.$submitted = false;
                        var fn = $parse(iAttrs.validSubmit);
                        element.on('submit', function(event) {
                            scope.$apply(function() {
                                element.addClass('ng-submitted');
                                form.$submitted = true;
                                if(form.$valid) {
                                    fn(scope, {$event:event});
                                }
                            });
                        });
                        scope.$watch(function() { return form.$valid}, function(isValid) {
                            if(form.$submitted == false) return;
                            if(isValid) {
                                element.removeClass('has-error').addClass('has-success');
                            } else {
                                element.removeClass('has-success');
                                element.addClass('has-error');
                            }
                        });
                    }
                }
            }
        }
    }]

    app.directive('validSubmit', ValidSubmit);

and then in my HTML:

<form class="form-horizontal" role="form" name="form" novalidate valid-submit="connect()">
  <div class="form-group">
    <div class="input-group col col-sm-11 col-sm-offset-1">
      <span class="input-group-addon input-large"><i class="glyphicon glyphicon-envelope"></i></span>
      <input class="input-large form-control" type="email" id="email" placeholder="Email" name="email" ng-model="email" required="required">
    </div>
    <p class="col-sm-offset-3 help-block error" ng-show="form.$submitted && form.email.$error.required">please enter your email</p>
    <p class="col-sm-offset-3 help-block error" ng-show="form.$submitted && form.email.$error.email">please enter a valid email</p>
  </div>
</form>

UPDATED

In my latest project, I use Ionic so I have the following, which automatically puts .valid or .invalid on the input-item's:

.directive('input', ['$timeout', function ($timeout) {
  function findParent(element, selector) {
    selector = selector || 'item';
    var parent = element.parent();
    while (parent && parent.length) {
      parent = angular.element(parent);
      if (parent.hasClass(selector)) {
        break;
      }
      parent = parent && parent.parent && parent.parent();
    }
    return parent;
  }

  return {
    restrict: 'E',
    require: ['?^ngModel', '^form'],
    priority: 1,
    link: function (scope, element, attrs, ctrls) {
      var ngModelCtrl = ctrls[0];
      var form = ctrls[1];

      if (!ngModelCtrl || form.$name !== 'form' || attrs.type === 'radio' || attrs.type === 'checkbox') {
        return;
      }

      function setValidClass() {
        var parent = findParent(element);
        if (parent && parent.toggleClass) {
          parent.addClass('validated');
          parent.toggleClass('valid', ngModelCtrl.$valid && (ngModelCtrl.$dirty || form.$submitted));
          parent.toggleClass('invalid', ngModelCtrl.$invalid && (ngModelCtrl.$dirty || form.$submitted));
          $timeout(angular.noop);
        }
      }

      scope.$watch(function () {
        return form.$submitted;
      }, function (b, a) {
        setValidClass();
      });


      var before = void 0;
      var update = function () {
        before = element.val().trim();
        ngModelCtrl.$setViewValue(before);
        ngModelCtrl.$render();
        setValidClass();
      };
      element
        .on('focus', function (e) {
          if (ngModelCtrl.$pristine) {
            element.removeClass('$blurred');
          }

        })
        .on('blur', function (e) {
          if (ngModelCtrl.$dirty) {
            setValidClass();
            element.addClass('$blurred');
          }
        }).on('change', function (e) {
          if (form.$submitted || element.hasClass('$blurred')) {
            setValidClass();
          }
        }).on('paste', function (e) {
          if (form.$submitted || element.hasClass('$blurred')) {
            setValidClass();
          }
        })
      ;

    }
  };
}])

and then in the HTML:

    <form name='form' novalidate="novalidate" ng-submit="auth.signin(form, vm)">
          <label class="item item-input item-floating-label">
            <span class="input-label">Email</span>
            <input type="email" placeholder="Email" ng-model="vm.email" autofocus="true" required
              >
          </label>
          <button ng-if="!posting" type="submit" class="item button-block item-balanced item-icon-right  call-to-action">Login<i class="icon ion-chevron-right"></i>
          </button>

and in the controller:

  self.signin = function (form, data) {
    if (!form.$valid) return;

    Authentication.emailLogin(data)
    //...

so, now, in the CSS, you can do stuff like:

.item.valid::before{
    float: right;
    font-family: "Ionicons";
    font-style: normal;
    font-weight: normal;
    font-variant: normal;
    text-transform: none;
    text-rendering: auto;
    line-height: 1;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    color: #66cc33;
    margin-right: 8px;
    font-size: 24px;
    content: "\f122";
}

.item.invalid::before{
    float: right;
    font-family: "Ionicons";
    font-style: normal;
    font-weight: normal;
    font-variant: normal;
    text-transform: none;
    text-rendering: auto;
    line-height: 1;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    color: #ef4e3a;
    margin-right: 8px;
    font-size: 24px;
    content: "\f12a";

/*
    border-left: solid 2px #ef4e3a !important;
    border-right: solid 2px #ef4e3a !important;
*/
}

MUCH SIMPLER!


A complete solution to the validate form with angularjs.

HTML is as follows.

<div ng-app="areaApp" ng-controller="addCtrler">
        <form class="form-horizontal" name="fareainfo">
            <div class="form-group">
                  <label for="input-areaname" class="col-sm-2 control-label">Area Name : </label>
                  <div class="col-sm-4">
                      <input type="text" class="form-control" name="name" id="input-areaname" ng-model="Area.Name" placeholder="" required>
                      <span class="text-danger" ng-show="(fareainfo.$submitted || fareainfo.name.$dirty) && fareainfo.name.$error.required"> Field is required</span>
                  </div>
            </div>
             <div class="col-sm-12">
                  <button type="button" class="btn btn-primary pull-right" ng-click="submitAreaInfo()">Submit</button>
             </div>
        </form>
    </div>

AngularJS App and Controller is as follows

var areaApp = angular.module('areaApp', []); 
areaApp.controller('addCtrler', function ($scope) {
    $scope.submitAreaInfo = function () {  
       if ($scope.fareainfo.$valid) {
         //after Form is Valid
       } else {
          $scope.fareainfo.$setSubmitted();
       }
    };
});

Important Code Segments

  1. ng-app="areaApp" ng-controller="addCtrler"
    Defines the angular app and controller

  2. ng-show="(fareainfo.$submitted || fareainfo.name.$dirty) && fareainfo.name.$error.required"
    Above condition ensure that whenever a user first sees the form there's no any validation error on the screen and after a user does changes to the form it ensure that validation message show on the screen. .name. is the name attribute of the input element.

  3. $scope.fareainfo.$valid
    Above code, segment check whether the form is valid whenever a user submits the form.

  4. $scope.fareainfo.$setSubmitted();
    Above code, segment ensures that all validation messages are displayed on the screen whenever a user submits the form without doing anything.


G45,

I faced same issue , i have created one directive , please check below hope it may be helpful

Directive :

    app.directive('formSubmitValidation', function () {

        return {
            require: 'form',
            compile: function (tElem, tAttr) {

                tElem.data('augmented', true);

                return function (scope, elem, attr, form) {
                    elem.on('submit', function ($event) {
                        scope.$broadcast('form:submit', form);

                        if (!form.$valid) {
                            $event.preventDefault();
                        }
                        scope.$apply(function () {
                            scope.submitted = true;
                        });


                    });
                }
            }
        };


  })

HTML :

<form  name="loginForm" class="c-form-login" action="" method="POST" novalidate="novalidate" form-submit-validation="">

<div class="form-group">
                                                <input type="email" class="form-control c-square c-theme input-lg" placeholder="Email" ng-model="_username" name="_username" required>
                                                <span class="glyphicon glyphicon-user form-control-feedback c-font-grey"></span>
                                                <span ng-show="submitted || loginForm._username.$dirty && loginForm._username.$invalid">
                                                    <span ng-show="loginForm._username.$invalid" class="error">Please enter a valid email.</span>
                                                </span>
                                            </div>
<button type="submit" class="pull-right btn btn-lg c-theme-btn c-btn-square c-btn-uppercase c-btn-bold">Login</button>
</form>

http://jsfiddle.net/LRD5x/30/ A simple solution.

HTML

<form ng-submit="sendForm($event)" ng-class={submitted:submitted}>

JS

$scope.sendForm = function($event) {
  $event.preventDefault()
  $scope.submitted = true
};

CSS

.submitted input.ng-invalid:not(:focus) {
    background-color: #FA787E;
}

input.ng-invalid ~ .alert{
    display:none;
}
.submitted input.ng-invalid ~ .alert{
    display:block;
}

I can come up with 2 ways to achieve it.

The first one is to remove novalidate to enable the browser's validation.

Second, you can disable the save button when the form is not valid like this

<input ng-disabled="!frmRegister.$valid" type="submit" value="Save" />

Hope it helps.


You only need to check if the form is dirty and valid before submitting it. Checkout the following code.

<form name="frmRegister" data-ng-submit="frmRegister.$valid && frmRegister.$dirty ? register() : return false;" novalidate>      

And also you can disable your submit button with the following change:

<input type="submit" value="Save" data-ng-disable="frmRegister.$invalid || !frmRegister.$dirty" />

This should help for your initial


Try this code:

<INPUT TYPE="submit" VALUE="Save" onClick="validateTester()">

This funvtion will validate your result

function validateTester() {
    var flag = true
    var Tester = document.forms.Tester
    if (Tester.line1.value!="JavaScript") {
        alert("First box must say 'JavaScript'!")
        flag = false
        }
    if (Tester.line2.value!="Kit") {
        alert("Second box must say 'Kit'!")
        flag = false
        }
    if (flag) {
        alert("Form is valid! Submitting form...")
        document.forms.Tester.submit()
        }
    }

// This worked for me.
<form  name="myForm" class="css-form" novalidate ng-submit="Save(myForm.$invalid)">
<input type="text" name="uName" ng-model="User.Name" required/>
<span ng-show="User.submitted && myForm.uName.$error.required">Name is required.</span>
<input ng-click="User.submitted=true" ng-disabled="User.submitted && tForm.$invalid" type="submit" value="Save" />
</form>
// in controller
$scope.Save(invalid)
{
if(invalid) return;
// save form
}

My solution with bootstrap 3

http://jsfiddle.net/rimian/epxrbzn9/

<form class="form" name="form" ng-app novalidate>
  <div class="form-group">
    <input name="first_name"
          type="text"
          class="form-control"
          ng-model="first_name"
          placeholder="First Name"
          required />
  </div>
  <div class="form-group">
    <input name="last_name"
          type="text"
          class="form-control"
          ng-model="last_name"
          placeholder="Last Name"
          required />
  </div>

  <button
    type="submit"
    class="btn btn-primary btn-large"
    ng-click="submitted=true">
      Submit
  </button>

<div ng-show="submitted && form.$invalid" class="alert alert-danger">
  <div ng-show="form.first_name.$error.required">
    First Name is Required
  </div>
  <div ng-show="form.last_name.$error.required">
    Last Name is Required
  </div>
</div>

</form>

There are two simple & elegant ways to do it.

Pure CSS:

After first form submission, despite the form validity, Angular will add a ng-submitted class to all form elements inside the form just submitted.

We can use .ng-submitted to controller our element via CSS.

if you want to display an error text only when user have submitted e.g.

.error { display: none }
.ng-submitted .error {
     display: block;
}

Using a value from Scope:
After first form submission, despite the form validity, Angular will set [your form name].$submitted to true. Thus, we can use that value to control elements.

<div ng-show="yourFormName.$submitted">error message</div>
<form name="yourFormName"></form>

I also had the same issue, I solved the problem by adding a ng-submit which sets the variable submitted to true.

<form name="form" ng-submit="submitted = true" novalidate>
<div>
    <span ng-if="submitted && form.email.$error.email">invalid email address</span> 
    <span ng-if="submitted && form.email.$error.required">required</span>
    <label>email</label>
    <input type="email" name="email" ng-model="user.email" required>
</div>

<div>
    <span ng-if="submitted && form.name.$error.required">required</span>
    <label>name</label>
    <input type="text" name="name" ng-model="user.name" required>
</div>

<button ng-click="form.$valid && save(user)">Save</button>
</form>

I like the idea of using $submitted, I think I've to upgrade Angular to 1.3 ;)


Examples related to javascript

need to add a class to an element How to make a variable accessible outside a function? Hide Signs that Meteor.js was Used How to create a showdown.js markdown extension Please help me convert this script to a simple image slider Highlight Anchor Links when user manually scrolls? Summing radio input values How to execute an action before close metro app WinJS javascript, for loop defines a dynamic variable name Getting all files in directory with ajax

Examples related to validation

Rails 2.3.4 Persisting Model on Validation Failure Input type number "only numeric value" validation How can I manually set an Angular form field as invalid? Laravel Password & Password_Confirmation Validation Reactjs - Form input validation Get all validation errors from Angular 2 FormGroup Min / Max Validator in Angular 2 Final How to validate white spaces/empty spaces? [Angular 2] How to Validate on Max File Size in Laravel? WebForms UnobtrusiveValidationMode requires a ScriptResourceMapping for jquery

Examples related to angularjs

AngularJs directive not updating another directive's scope ERROR in Cannot find module 'node-sass' CORS: credentials mode is 'include' CORS error :Request header field Authorization is not allowed by Access-Control-Allow-Headers in preflight response WebSocket connection failed: Error during WebSocket handshake: Unexpected response code: 400 Print Html template in Angular 2 (ng-print in Angular 2) $http.get(...).success is not a function Angular 1.6.0: "Possibly unhandled rejection" error Find object by its property in array of objects with AngularJS way Error: Cannot invoke an expression whose type lacks a call signature