AngularJS - form validation, v 1.4.8 - javascript

This is my first time using AngularJS, and the form validation is making me question my sanity. You would think this would be the easy part, but no matter how many ways I've tried Googling, the only thing that works is if I set a flag inside my controller's submit if the form is invalid to set the error class. I've looked at similar problems here, but none of them helped, so please do not simply dismiss this as a potential duplicate. Everything else has been a fail.
In the example mark up below I have reduced my form down to just one element. Here is what I have observed:
Using only $error.required does work. The ng-class { 'has-error' :registerForm.firstName.$error.required} does outline the text box with the bootstrap has-ertror class, but this is on form load, which I do not want.
The <p> element with the error message will exhibit the same behavior, so I know that the message exists and is not malfored. It will also display if I only use $error.required. But as soon as I add && registerForm.$submitted ( or $isdirty or !notpristine ) the message will not display on form submit. There are no errors (have developers tools open in chrome) and will post to the web API with no problem and return ok 200 or 400 if I send bad params.
I can write validation code inside my controller, checking if the field has a value and setting a flag on $scope such as $scope.firstNameIsRequired and that will work fine setting ng-show="$scope.firstNameIsRequired", but that will remove testability.
So the problem definitely has to be with how I am adding this in the markup. But after a weekend spent googling I am at my wits end. The only other thing different is that I am using a span on a click element to submit the form instead of an input = submit, but the registerForm.$valid function is setting the correct value. Do I somehow need to trigger the form validation in that ng-click directive?
I am using angular.js v 1.4.8.
I do have angular ui which has it's own validate, but that shouldn't interfere with the basic validation.
Here is the simplified markup:
<form name="registerForm" class="form-group form-group-sm"
ng-controller="userAccountController" novalidate>
<div class="form-group"
ng-class="{ 'has-error' : registerForm.firstName.$error.required }">
<div><label>First Name</label> </div>
<input type="text" class="form-control" id="firstName" name="firstName" value=""
ng-model="firstName" placeholder="First Name" maxlength="100" required=""/>
<p ng-show="registerForm.firstName.$error.required && registerForm.$submitted"
class="alert alert-danger">First Name is required</p>
</div>
<div>
<span class="btn btn-default"
ng-click="submit(registerForm.$valid)">Register</span>
</div>
My controller code is
angular.module( "Application" ).controller( "userAccountController", [
"$scope", "userAccountService", function ( $scope, userAccountService)
{
$scope.hasErrors = false;
$scope.errorMessages = "";
$scope.emailExists = true;
$scope.clearErrors = function (){
$scope.hasErrors = false;
}
$scope.onSuccess = function ( response ) {
alert( "succeeded" );
}
$scope.submit = function (isValid) {
if ($scope.registerForm.$invalid)
return;
alert("isvalid");
$scope.clearErrors();
var userProfile = $scope.createUser();
userAccountService.registerUser(userProfile, $scope.onSuccess, $scope.onError);
}
$scope.createUser = function () {
return {
FirstName: $scope.firstName, LastName: $scope.lastName, Email: $scope.email,
Password: $scope.password, SendAlerts: $scope.sendAlerts
};
};
}
]);
Any help will be appreciated. I probably just need a second set of eyes here because I have been dealing with this on and off since late Friday.

in angular you want use the element.$valid to check wheter an model is valid or not - and you use element.$error.{type} to check for a specific validation error.
Keep in mind that the form.$submitted will only be set if the form is actually submitted - and if it has validationerrors it will not be submitted (and thus that flag is still false)
If you want to show errors only on submit you could use a button with type="submit" and bind to ng-click event - and use that to set a flag that the form has been validated. And handling the submit if the form is valid.
A short example with 2 textboxes, having required and minlength validation:
angular.module("myApp", [])
.controller("myFormController", function($scope) {
$scope.isValidated = false;
$scope.submit = function(myForm) {
$scope.isValidated = true;
if(myForm.$valid) {
console.log("SUCCESS!!");
}
};
});
.form-group {
margin: 10px;
padding: 10px;
}
.form-group.has-error {
border: 1px solid red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.8/angular.js"></script>
<div ng-app="myApp" ng-controller="myFormController">
<form name="myForm">
<div class="form-group" ng-class="{'has-error': myForm.name.$invalid && isValidated}">
<span>Name:</span>
<input type="text" name="name" minlength="5" ng-model="name" required />
<span ng-if="myForm.name.$error.required && isValidated">Name is required</span>
<span ng-if="myForm.name.$error.minlength && isValidated">Length must be atleast 5 characters</span>
</div>
<div class="form-group" ng-class="{'has-error': myForm.email.$invalid && isValidated}">
<span>Email:</span>
<input type="text" name="email" minlength="5" ng-model="email" required />
<span ng-if="myForm.email.$error.required && isValidated">Email is required</span>
<span ng-if="myForm.email.$error.minlength && isValidated">Length must be atleast 5 characters</span>
</div>
<button type="submit" ng-click="submit(myForm)">Submit</button>
</form>
</div>

Related

Form submits multiple times

I am using a Wordpress theme that unfortunately is duplicating the header HTML for desktop, mobile and tablet. As a result, a login form I have appears to be submitting multiple times even though "Login" is only clicked once.
Here is the HTML for the form:
<div id="user-login">
<div class="com_row">
<div class="com_panel_body">
<div id="error_message91" class="com_alert com_alert_danger" style="display: none;">
</div>
<form method="post" id="validation_form83">
<input type="hidden" name="login_form_flag" value="1">
<div class="login-username">
<label for="email" class="main_label">Email Address</label>
<input id="email68" type="email" name="email" required="required">
</div>
<div class="login-password">
<label for="password" class="main_label">Password:</label>
<input id="password82" type="password" name="password" required="required">
</div>
<ul class="login-links" style="margin-top:-30px"><li>Forgot Password?</li></ul>
<div class="login-submit" style="margin-top:-20px">
<input type="submit" value="Login"></div>
<div style="padding-top:20px"><a class="button green small borderd-bot" href="/client_account">Register</a></div>
</form>
</div>
</div>
</div>
Here is the relevant JS:
$("[id^='validation_form']").each(function(i) {
//necessary because there are 3 form duplicates on the page, so this button works on all
jQuery(document).on("submit", this, SubmitValidationForm);
});
function($) {
SubmitValidationForm = function (event) {
event.preventDefault();
var formk = "#"+event.target.id;
var k = $(formk).serialize();
k += "&action=wcap_requests&what=validate_login";
jQuery("input[type=email]",formk).prop("disabled", true);
jQuery("input[type=password]",formk).prop("disabled", true);
jQuery("input[type=submit]",formk).prop("disabled", true).val(WCAP_Working_text);
var childf = $(formk).closest('div','.com_alert').children( ".com_alert");
$(childf).hide();
var login_form_flag = jQuery("input[name=login_form_flag]",formk).val();
jQuery.post(wcap_ajaxurl, k, function (data) {
data = JSON.parse(data);
console.log(data);
if (data.status === "OK") {
//== if client login through wcap login form
if (login_form_flag === '1'){
window.location.href = client_area_url;
}
else {
if (redirect_login !== "0") {
window.location.href = redirect_login;
} else {
window.location.reload();
}
}
}
else {
jQuery("input[type=email]",formk).prop("disabled", false);
jQuery("input[type=password]",formk).prop("disabled", false);
jQuery("input[type=submit]",formk).prop("disabled", false).val('Login');
$(childf).html(data.message).show();
}
});
};
};
The problem is because there are 3 duplicate forms on the page HTML (with only 1 visible to the user), the SubmitValidationForm function is called 3 times every time. The issue is pronounced when there is a valid login submitted, but the error box still appears saying invalid email after a few seconds (even though the login is actually correct and the user gets automatically redirected properly to the client area ). This error seems caused by the fact the SubmitValidationForm function is called 2 subsequent times after the first 'valid' submission which makes it think it's invalid, when it's not... the interesting thing is it doesn't seem caused by the other duplicate forms in the HTML, as the form ID attribute that I display in browser console shows only the 'valid' form being submitted (albeit multiple times -- perhaps because of the jquery.on() for each function).
Any ideas how to fix?
Thanks!
I figured out the issue. If anyone else is looking at this in future the issue was with respect to the 'on' function, it was referencing the 'document' before instead of 'this'. So it should be changed to:
$("[id^='validation_form']").each(function(i) {
jQuery(this).on("submit", this, SubmitValidationForm);
});

how to not validate hidden field in ng-click?

when user click on submit button i am validating form, using ng-click i am calling function, in this function i am passing form1.$invalid,based on this variable , i am putting condition, if condition true, validate function will call, here problem is mobile is hidden field, this hidden field also checking validation.how can skip or not validate mobile field hidden status, I tried bellow code.
html
----
<form name="form1" novalidate>
<input ng-show="user" type="text" name="user" ng-model="frm1.user" />
<p ng-show="form1.user.$error.required"><span ng-show="errorMsgShow" ng-required="true">{{requiredMsg}}</span></p>
<input ng-show="mobile" type="text" name="mobile" ng-model="frm1.mobile" />
<p ng-show="form1.mobile.$error.required"><span ng-show="errorMsgShow" ng-required="true">{{requiredMsg}}</span></p>
<button ng-click="SubmitForm(regForm.$invalid);">submit</button>
</form>
Script----
$scope.SubmitForm = function(val){
$scope.user= true;
$scope.mobile = false;
if (if(val ===true){
$scope.validation();
}
}
$scope.validation = function(){
$scope.requiredMsg="input fieldis required";
}
I suggest better approach will be taking the mobile input out of the form when it's unnecessary using ng-if rather than just hide it with ng-show.
Ng-if will make sure the input is not rendered in the DOM tree when the condition is false, therefore, there will be no validation triggered.
You can do some research on differences between ng-if and ng-show to have better understanding about these two directives.
Try ng-if to avoid validation.If you want mobile to skip validation then make ng-if as false using expression.
syntax: ng-if="expression"
go to this link for further info
https://docs.angularjs.org/api/ng/directive/ngIf
for difference between ng-if and ng-hide/ng-show refer the below link
what is the difference between ng-if and ng-show/ng-hide
Some observations :
Instead of using ng-show to hide and show the inputs just use <input type="hidden"... > element for mobile field.
No need to use variable $scope.user and $scope.mobile to hide and show the inputs.
Add the required attribute in the user input field not on mobile input field as you don't want to validate mobile field.
Use SubmitForm(form1.$invalid) instead of SubmitForm(regForm.$invalid) as your form name is form1 not regForm.
DEMO
var myApp = angular.module('myApp',[]);
myApp.controller('MyCtrl', function($scope) {
$scope.SubmitForm = function(val) {
console.log(val);
if(val === true) {
$scope.validation();
}
}
$scope.validation = function() {
$scope.requiredMsg="input fieldis required";
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="MyCtrl">
<form name="form1" novalidate>
<input type="text" name="user" ng-model="frm1.user" required/>
<p ng-show="form1.user.$error.required">{{requiredMsg}}</p>
<input type="hidden" name="mobile" ng-model="frm1.mobile" />
<button ng-click="SubmitForm(form1.$invalid);">submit</button>
</form>
</div>

Form data object is not formed on first click when validation fails; only on second click

I'm using a pretty simple form to submit something to our API. I added some form validation using a directive called Angular UI Form Validation. I'm just submitting an object with 2 properties; 'id', and 'points'. I'm running into a strange issue that I've been having a lot of trouble solving and hope I can get some help here.
The form validation works fine. If I enter a value into the input field that doesn't pass, you can't submit the form and a message beneath the input saying why appears. However, if I then remove the incorrect input and put in a correct input, the message is still there. If I click on the submit button, the message disappears. If I click the submit button again, it submits. I've noticed, however, that if I just press the enter key it submits right away.
I threw some console.log's in there, and it looks like the problem is that a formData object isn't created on the first click; only on the second. Here is the html:
<form name="pointsField">
<p><input type="number" ng-model="formData.points"
validation-only-numbers="true"
validation-field-required="{message:'Points value is required.',value:true}"
min="0"/></p>
<p><button class="btn btn-success" type="button" ng-click="updatePoints(pointsField)" ng-disabled="loading">
<span ng-if="loading"><i class="fa fa-circle-o-notch fa-la fa-spin"></i></span>Update Points
</button></p>
</form>
And here is the Javascript (keep in mind that the formData object is created when the controller is initialized, and it is just an empty object):
$scope.updatePoints = function(formData) {
console.log('formData ', formData);
$scope.$broadcast('runCustomValidations');
if(formData.$invalid){
return;
}
var pointsObject = {
"id": $scope.userId,
"points": $scope.formData.points
};
$scope.loading = true;
User.updateUserPoints(pointsObject).then(function(response) {
if(response.success) {
Notification.now.success(response.message);
$scope.loading = false;
} else {
Notification.now.error(response.message);
$scope.loading = false;
}
}, function(reason) {
Notification.now.error(reason.message);
$scope.loading = false;
});
};
If anyone can help me out I'd be eternally grateful. Thank you!
try adding novalidate in your form tag if this does not work dont use angular ui Form validation just use below format to validate .
<form name="pointsField" novalidate>
<input type="text" id="username" name="username" placeholder="Email Address"
ng-model="user.username" autocomplete="off" required >
<span class="text-danger" ng-show="pointsField.username.$dirty && pointsField.username.$invalid">
<span ng-show="pointsField.username.$error.required">Email Address is required</span>
</span>
<button type="submit" class="submit center" ng-disabled="pointsField.$invalid || dataLoading ">
<form>

clearing ng-messages in controller

I'm new to working with ng-message. I have an error message (form.$error below) showing up when I don't want it to, while a user is typing and changing their incorrect entry. how can I either a) use ng-focus properly or b) clear ng-messages in my controller when a user is typing? my attempt at a) using ng-focus is below but it isn't working. I also tried replacing ng-focus with ng-change but that still doesn't work. is it also possible to manually disable the error message from showing up in the controller by clearing ng-message?
my html. I attempt to set the form to valid when the user is typing.
<div class="col-xs-3">
<input ng-focus="setValid()"
class="form-control" required ng-blur="checkCodes()"
ng-model="code1">
</div>
further down in my html I have this other div where the error shows up when I don't want it to, when the user is typing.
<div ng-if="codeError"
ng-messages="form.$error">
<p ng-show="code1 === code2"
class="disabled-text long-error">Inputs can't be the same</p>
</div>
this is the main js in my controller:
$scope.checkCodes = function() {
if ($scope.code1 && $scope.code1 === $scope.code2) {
$scope.showUniqueError = true;
$scope.form.$setValidity("prop", false);
$scope.showError2 = false;
} else {
$scope.showUniqueError = false;
$scope.form.$setValidity("prop", true);
}
}
//tried to use this in ng-focus but not working.
$scope.setValid = function() {
$scope.form.$setValidity("prop", true);
}
You were pretty close you just had the wrong scope variable in your ng-if.
I also change the ng-blur attritbutes to ng-keyup. With ng-blur your error message would only be displayed if you click outside of the text box or another control gets focus.
In the live example you will see that if you type in the same value for each input box that your error will be displayed and if you change one of the input boxes to a different value then the error will be removed.
Live Example: http://codepen.io/larryjoelane/pen/QyOZNR
test html:
<div ng-app="test" ng-controller="testController"><!--begin app container-->
<div class="col-xs-3">
<input ng-focus="setValid()"
class="form-control" required ng-blur="checkCodes()"
ng-model="code1">
<input ng-focus="setValid()"
class="form-control" required ng-blur="checkCodes()"
ng-model="code2">
</div>
<div ng-if="showUniqueError"
ng-messages="form.$error">
<p ng-show="code1 === code2"
class="disabled-text long-error">Inputs can't be the same</p>
</div>
</div><!--end app container-->
Test Javascript(Unchanged other then adding module wrapper and closure):
(function(){
angular.module("test",[]).controller("testController",function($scope){
$scope.checkCodes = function() {
if ($scope.code1 && $scope.code1 === $scope.code2) {
$scope.showUniqueError = true;
$scope.form.$setValidity("prop", false);
$scope.showError2 = false;
} else {
$scope.showUniqueError = false;
$scope.form.$setValidity("prop", true);
}
}
//tried to use this in ng-focus but not working.
$scope.setValid = function() {
$scope.form.$setValidity("prop", true);
}
});//end controller
})();
You can do it without using ng-message.
<form class="form-signin hide" id="gbLoginForm" method="POST" name="loginform" ng-controller="LoginCtrl" novalidate>
<div class="form-group">
<label for="username">User Name</label>
<input type="text" class="form-control" id="username" name="email" placeholder="User name/email" ng-model="user.email" required autofocus>
<span class="has-error" ng-show="loginform.email.$dirty && loginform.email.$error.required">
<span class="help-block" ng-bind="getValue('EMPTYEMAIL')"></span>
</span>
<span class="has-error" ng-show="loginform.email.$dirty && !loginform.email.$error.required && loginform.email.$error.validemail">
<span class="help-block" ng-bind="getValue('INVALIDEMAIL')"></span>
</span>
</div></form>
// email field is empty
if(!$scope.user.email){
$scope.loginform.email.$dirty = true;
$scope.loginform.email.$setValidity('required', false);
return false;
}
//invalid email
if(!UtilityService.validateEmail($scope.user.email)) {
$scope.loginform.email.$dirty = true;
$scope.loginform.email.$setValidity('validemail', false);
return false;
}
You don't need use ng-blur or ng-keyup, because angular refresh your ngModel on change input.
To create the various checks can use directive use-form-error.
Live example jsfiddle.
<form name="ExampleForm">
<label>Code 1</label>
<input ng-model="code1" required/>
<label>Code 2</label>
<input ng-model="code2" required/>
<div use-form-error="isSame" use-error-expression="code1 && code1==code2" ng-show="ExampleForm.$error.isSame">Inputs can't be the same</div>
</form>

Do not trigger form.$invalid on first load

Having such form
<div ng-controller="FormController as f_ctrl">
<form ng-submit="f_ctrl.submit()" name="myForm">
<input type="text" ng-model="f_ctrl.user.username"
required
ng-minlength="4"/>
<input type="text" ng-model="f_ctrl.user.password"/>
<input type="submit" value="Submit" ng-disabled="myForm.$invalid">
</form>
</div>
and such controller
.controller('FormController', [function() {
var self = this;
self.submit = function() {
console.log('User submitted form with ' + self.user.username)
}
}]);
I have a problem: when page first loads it immediately shows red border on username field, even before I start typing anything.
I need to highlight invalid fields only after first submission. Can this be done using form.$invalid ?
You have to use $pristine for that. It is true when form controller is not changed. so when you change textbox data its comes false.
Small example for you.
<div class="form-group" ng-class="{ 'has-error' : userForm.password.$invalid && !userForm.password.$pristine }">
<input id="passAnime" type="password" name="password" ng-model="user.password" class="form-control input-md" placeholder="Password" tabindex="5" ng-maxlength="25" ng-minlength="6" required>
<span ng-show="userForm.password.$dirty && userForm.password.$invalid">
<p ng-show="userForm.password.$error.required" class="error-messages">
Your password is required.
</p>
<p ng-show="userForm.password.$error.minlength" class="error-messages">
Your password is too short. Minimum 6 chars.
</p>
<p ng-show="userForm.password.$error.maxlength" class="error-messages">
Your password is too long. Maximum 25 chars.
</p>
</span>
</div>
Angular has helpers that tell you if the form (or form field) is $dirty (user has typed something) or if the form is $touched (the blur event has been triggered on the input). See this demo.
I need to highlight invalid fields only after first submission.
Unfortunately, Angular doesn't support that. But you could implement it yourself rather easily:
Controller
function FormController() {
var vm = this;
vm.submitAttempted = false;
vm.submit = function(isValid) {
if (isValid) {
// do stuff
}
else {
vm.submitAttempted = true;
}
};
}
HTML
<div ng-app='app'>
<div ng-controller='FormController as vm'>
<form name='fooForm' ng-submit='vm.submit(fooForm.$valid)' novalidate>
<label>Username</label>
<input
name='username'
type='text'
ng-model='vm.user.username'
required
ng-minlength='4'
ng-class="{'invalid': vm.submitAttempted && fooForm.username.$invalid}">
<br /><br />
<button type='submit'>Submit</button>
</form>
</div>
</div>
CSS
.invalid {
border-color: red;
}
Demo
I have a problem: when page first loads it immediately shows red border on username field, even before I start typing anything.
That's probably because you have the following CSS class:
.ng-invalid {
border-color: red;
}
Angular will always apply the ng-invalid class to fields that are invalid, and there's nothing you could do about that. So if you don't always want invalid fields to have a red border, you can't use that class and you should do it in a way similar to what I proposed above.
Also, check out ngMessages.
You can disable the default styling on the input field that is adding the red border by default, by adding the following CSS:
input:required {
-moz-box-shadow: none;
box-shadow: none;
}
Then if you want to highlight the field when the form is submitted, you will need to ensure that the form and form fields have relevant name attributes. Doing this will allow you to check if the field is valid or not and apply a class to your text field when it is invalid:
<input type="text" name="username" ng-class="{ 'invalid-field' : f_ctrl.myForm.username.$invalid && !f_ctrl.myForm.username.$pristine }" required />
f_ctrl.myForm and f_ctrl.myform.username will have additional properties that you can use/check to determine if the form or fields are invalid or not, or if they have been modified at any point (e.g. f_ctrl.myform.username.$dirty). You should be able to view these properties on your page by adding the follow HTML:
<div>
<pre>{{f_ctrl.myForm | json}}</pre>
</div>
Or, you could output self.myForm to the console from your controller to view it's properties
console.log(self.myForm);

Categories