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
in controller: $scope. errors = []; $scope. hasError = false; $scope.
There are no fixed ways to show errors in HTML forms, but the common methods to display error messages are: Simply add checking attributes to the HTML form fields, and the browser will automatically show the errors. For example, <input type="text" required/>
The form instance can optionally be published into the scope using the name attribute. So to check form validity, you can check value of $scope. yourformname. $valid property of scope.
I found this fiddle http://jsfiddle.net/thomporter/ANxmv/2/ which does a nifty trick to cause control validation.
Basically it declares a scope member submitted
and sets it true when you click submit. The model error binding use this extra expression to show the error message like
submitted && form.email.$error.required
UPDATE
As pointed out in @Hafez's comment (give him some upvotes!), the Angular 1.3+ solution is simply:
form.$submitted && form.email.$error.required
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!
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 ;)
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>
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>
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
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 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);
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();
}
};
});
ng-app="areaApp" ng-controller="addCtrler"
Defines the angular app and controller
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.
$scope.fareainfo.$valid
Above code, segment check whether the form is valid whenever a user submits the form.
$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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With