I have an input form that I'm performing client-sided validation on with the jQuery validator plugin. Basic usage is working great, except for a specific scenario:
The form splits up address input fields, allowing separate fields for street number, name, city, state, and zip. The address itself is an optional input to the form (a user may opt to enter no address), but I want to ensure that if any one of these fields are used, the user is prompted to enter all the fields.
This works, except in the case when someone enters in an address and hits submit, and then decides to enter in no address. The ideal behavior in this case would be that, as soon as the text in the inputs they've entered is removed, for the address group to be unhighlighted.
Here is the current scenario:
User enters information into only one input field, e.g., street name.
The submit button is clicked.
The validator plugin highlights the other address inputs with an error message prompting for the full address.
User decides to enter no address, and removes the prior input, e.g. erases street name
Ideally: All the other highlighted address inputs are unhighlighted and the error message is removed. Actually: The highlighted address inputs and message remain until form submission.
Here is the javascript that demonstrates the problem and the corresponding JSFiddle.
$("form").validate({
errorClass: 'error',
errorElement: 'label',
submitHandler: function() {
alert("Form submitted");
return false;
},
groups: {
address: "streetNumber streetName city state zipcode"
},
rules: {
streetNumber: {
required: {
depends: function(){
return $("#streetName").val() != '' || $("#city").val() != '' || $("#state").val() != '' || $("#zipcode").val() != '';
}
}
},
streetName: {
required: {
depends: function(){
return $("#streetNumber").val() != '' || $("#city").val() != '' || $("#state").val() != '' || $("#zipcode").val() != '';
}
}
},
city: {
required: {
depends: function(){
return $("#streetNumber").val() != '' || $("#streetName").val() != '' || $("#state").val() != '' || $("#zipcode").val() != '';
}
}
},
state: {
required: {
depends: function(){
return $("#streetNumber").val() != '' || $("#streetName").val() != '' || $("#city").val() != '' || $("#zipcode").val() != '';
}
}
},
zipcode: {
required: {
depends: function(){
return $("#streetNumber").val() != '' || $("#streetName").val() != '' || $("#city").val() != '' || $("#state").val() != '';
}
}
}
},
messages: {
streetNumber: {required: "Must provide full address"},
streetName: {required: "Must provide full address"},
city: {required: "Must provide full address"},
state: {required: "Must provide full address"},
zipcode: {required: "Must provide full address"}
},
highlight: function(element, errorClass, validClass) {
$(element).addClass(errorClass).removeClass(validClass);
},
unhighlight: function(element, errorClass, validClass) {
$(element).removeClass(errorClass);
},
errorPlacement: function(error, element) {
var n = element.attr("name");
if (n == "streetNumber" || n == "streetName" || n == "city" || n == "state" || n == "zipCode")
error.insertAfter("#zipcode");
}
});
Besides trying to get the desired functionality of the highlight, I'm also wondering if there is a smarter way to accomplish the "all or nothing" input groups that doesn't involve the mess of conditional statements. Perhaps I can use a form input group?
You need to add a function on the focus event, then when the user leaves the field, the form fields will update.
It's difficult because you are using a plugin, so all the calls are happening inside that but I think something like this will work:
var inputs = $('form').find('input');
inputs.focus(function () {
inputs.each(function () {
$(this).removeClass('error');
});
});
Just stick this in your code outside of the validate initialiser.
It would be even better if you defined your errorCode variable outside of the validator and then used that var in both functions, like this:
var errorClass = 'error';
$('form').validate({
errorClass: errorClass,
...
...
});
var inputs = $('form').find('input');
inputs.focus(function () {
inputs.each(function () {
$(this).removeClass(errorClass);
});
});
you can try to add a method in the
unhighlight:
something like
$('.error').removeClass(errorClass);
or define related inputs and do
$('.relatedInputs').removeClass(errorClass);
you could also add an onChange function like
function(el){
if(el.val() == ''){
$('.relatedInputs').removeClass(errorClass);
}
}
I was able to get desired functionality by using this:
onfocusout: function(element) {
var inputs = $(element).closest('form').find('input, select');
inputs.each(function() {
if ($(this).valid())
$(this).removeClass('error');
});
}
Which was inspired from another post and DoubleA's answer. I haven't tested it thoroughly to see if it regresses anything, but so far it seems to work.
Related
I have a submit button that is initially disabled (through v-bind:disabled) and will only be enabled until all form inputs are non-empty and errors are resolved. For each of my input, I have a spinner that will become a check or an x after verifying the validity of the input. This takes a few seconds and I was hoping that the same delay be applied to the enabling of the submit button. At the moment, this is what the form looks like:
This is the form (in pug):
.form-group
label Email Address
.input-group
input.form-control(type="email" name="emailAddress" value=profile.email
v-model="email"
v-validate
data-vv-delay="1000"
data-vv-rules="required|email"
data-vv-as="email"
:class="{ 'input': true, 'is-danger': errors.has('emailAddress') }"
placeholder="eg. andres#gmail.com")
.input-group-append
span.input-group-text
i.fal.fa-pulse.fa-spinner(v-if="email && emailBusy")
i.fal.fa-check.text-green(v-if="email && !emailBusy && !isEmailTaken && !errors.has('emailAddress')")
i.fal.fa-times.text-red(v-if="email && !emailBusy && (isEmailTaken || errors.has('emailAddress'))")
span.text-danger.text-error(v-show="errors.has('emailAddress')") {{ errors.first('emailAddress') }}
span.text-danger.text-error(v-if="email && email.length > 0 && isEmailTaken") Email address has already been taken
.form-group
label Username
.input-group
input.form-control(type="name" name="username"
v-model="username"
v-validate
data-vv-delay="1000"
data-vv-rules="required|verify_username"
:class="{ 'input': true, 'is-danger': errors.has('username') }"
placeholder="eg. andres45")
.input-group-append
span.input-group-text
i.fal.fa-pulse.fa-spinner(v-if="username && usernameBusy")
i.fal.fa-check.text-green(v-if="username && !usernameBusy && !isUsernameTaken && !errors.has('username')")
i.fal.fa-times.text-red(v-if="username && !usernameBusy && (isUsernameTaken || errors.has('username'))")
span.text-danger.text-error(v-show="errors.has('username')") {{ errors.first('username') }}
span.text-danger.text-error(v-if="username && username.length > 0 && isUsernameTaken") Username has already been taken
.form-group
button.btn.btn-blue(:disabled="errors.any() || isEmailTaken || isUsernameTaken || !username || !email" type="submit")
i.fal.fa-sign-in.mr-2
span Complete Sign Up
The vue instance:
var register = new Vue({
el: '#register',
data: {
email: email,
username: null,
isUsernameTaken: false,
usernameTimer: null,
usernameBusy: false,
isEmailTaken: false,
emailTimer: null,
emailBusy: false
},
methods: {
validateEmail: function(email) {
var self = this;
var url = '/api/users?email=' + email;
self.$http.get(url)
.then(function(res){
self.isEmailTaken = true;
self.emailBusy = false;
}, function(err){
self.isEmailTaken = false;
self.emailBusy = false;
});
},
validateUsername: function(username) {
var self = this;
var url = '/api/users/' + username;
self.$http.get(url)
.then(function(res){
self.isUsernameTaken = true;
self.usernameBusy = false;
}, function(err){
self.isUsernameTaken = false;
self.usernameBusy = false;
});
}
},
watch: {
username: function(val) {
var self = this;
clearTimeout(self.usernameTimer);
self.usernameBusy = true;
self.usernameTimer = setTimeout(function() {
self.validateUsername(val);
}, 1600);
},
email: function(val) {
var self = this;
clearTimeout(self.emailTimer);
self.emailBusy = true;
self.emailTimer = setTimeout(function() {
self.validateEmail(val);
}, 1600);
}
}
});
I’m on mobile so apologies about formatting and lack of code. For me I would probably set up a variable to track the disabled state, say var isFormComplete = false; I would use the vue disabled prop to control the button/form state. :disabled=“!isFormComplete”.
Then I would set up a vue watch or even computed method in the JS which basically will check if each form control is empty or whatever arbitrary value checking you want to do, since it’s dynamic behind the scenes with variables it should be pretty simple to check through each form control and when all conditions are satisfied, set the isFormComplete or whatever you want to call it to true and your control will be enabled.
For extra fun and bonus points, you could set up some generic validation code to be reusable and abstract it out as a vue mixin and have yourself a nifty custom form validation you can reuse. I know this isn’t a traditional answer but since I’m mobile I felt this was more indepth than a comment even though there is a lack of code. You can add a timer as well in the validation method to when all conditions are satisfied delay by however long you want and then set the disabled variable to false.
I am having trouble submitting the below form.
For background, I'm trying to "submit" a form for a delivery, and I need to know a) their pickup address, b) their dropoff address, and c) their description. I created <p class="error"> fields if those <input>s are empty (as in "Please enter a description").
If I remove the 'return false;' the form submits no matter what, but if I keep the 'return false;' the jQuery works (i.e. - error message appears) but now the form NEVER submits. Thoughts?
Here's my main.js
var main = function() {
$('form').submit(function() {
var pickup = $('#pickup').val();
if(pickup === "") {
$('.pickup-error').text("Please choose a pickup.");
}
var dropoff = $('#dropoff').val();
if(dropoff === "") {
$('.dropoff-error').text("Please choose a dropoff.");
}
var description = $('#description').val();
if(description === "") {
$('.description-error').text("Please tell us a little about what we're moving.");
}
return false;
});
};
$(document).ready(main);
var main = function () {
$('form').submit(function () {
var pickup = $('#pickup').val();
if (pickup === "") {
$('.pickup-error').text("Please choose a pickup.");
}
var dropoff = $('#dropoff').val();
if (dropoff === "") {
$('.dropoff-error').text("Please choose a dropoff.");
}
var description = $('#description').val();
if (description === "") {
$('.description-error').text("Please tell us a little about what we're moving.");
}
// did not pass validation
if (pickup != "" || dropoff != "" || description != "") {
return false;
}
// passed validation, submit
return true;
});
};
$(document).ready(main);
I have a dynamic table, which each row contains country and numberOfState fields. Currently I am able to add new record and validate the country and numberOfState field separately (e.g. required) after click the "AddNewRecord" button, which is below code that generate dynamic table unique field name, i.e. name="country_0", "numberOfState_0" for 1st row, and ="country_1", "numberOfState_1" for 2nd row and etc.
Would like to check whether can validate the dynamic country and numberOfState fields together (i.e. Country is US and NumberOfState must be 50), using dynamic rule code as per below addRowRule function. Thanks in advance.
$(document).ready(function(e){
var rowindex = 0;
$("#AddNewRecord").click(function(){
var row =
"<tr><td>input name=country_"+rowindex+" type=text class='countryRule'/></td>
<tr><td>input name=numberOfState_"+rowindex+" type=text class='stateRule'/></td></tr>";
$("#table").append(row);
rowindex++;
addRowRule(rowindex);
});
jQuery.validate.addClassRules({
countryRule:{required:true},
stateRule:{required:true}
});
$('form').validate();
function addRowRule(i) {
var country = '#country_' + i,
var numberOfState = '#numberOfState_' + i;
$(country).rules('add', {
required: true,
numberOfState:{
required: {
depend: function(element){
if ($(country).val() == 'US' &&
$(numberOfState).val() !=50){
return false;
}
return true;
}
messages: {
numberOfState: "Number of state not match with country",
}
},
messages: {
required: "Required input",
}
});
});
Updated code to share with all:
$( document ).ready(function() {
$("#myForm").validate(); //sets up the validator
var rowindex = 0;
$("#AddNewRecord").click(function(){
var row = "<tr><td>input name=country_"+rowindex+" type=text /></td>" +
"<tr><td>input name=numberOfState_"+rowindex+" type=text /></td></tr>";
$("#table").append(row);
addRowRule(rowindex);
rowindex++;
});
function addRowRule(row_index) {
var country = '#country_' + row_index;
var numberOfState = '#numberOfState_' + row_index;
$(country).rules('add', {
required: true,
messages: {
required: "Pls input country."
}
});
$(numberOfState).rules('add', {
required: true,
checkCountryAndState: [country, numberOfState],
messages: {
required: "Pls input number of state."
}
});
}
jQuery.validator.addMethod("checkCountryAndState", function(value, element, params){
var varCountry = params[0];
var varNumberOfState = params[1];
if ($(varCountry).val() === 'America' && $(varNumberOfState).val() !== 50){
return false;
}
return true;
}, jQuery.format("Country is not match with Number of State."));
});
You can specify validation rules with the rules property. This should do what you specified in the question as an example:
$(".selector").validate({
rules: {
field2: {
required: true,
field1: {
depends: function(element) {
if ($('#field1').val() === 'A' && $('#field2').val() === 'Z') {
return false;
}
return true;
}
}
}
}
});
After this, you need to assign a message if the validation fails with the messages property.
Part of your problem is putting invalid objects inside of the .rules() method. Since the .rules() method is already attached to a selector (representing a SINGLE field), you cannot declare rules for additional fields inside of it...
function addRowRule(i) {
var country = '#country_' + i,
var numberOfState = '#numberOfState_' + i;
$(country).rules('add', {
required: true,
numberOfState: { // <- you can NOT put this here
required: { ...
The only objects allowed inside of .rules() is a key: value list of various rules/methods and/or the messages object.
You would have to attach other fields to different instances of .rules()....
function addRowRule(i) {
var country = '#country_' + i,
var numberOfState = '#numberOfState_' + i;
$(country).rules('add', {
required: true,
....
});
$(numberOfState).rules('add', {
required: true,
....
});
....
This question already has answers here:
jQuery Validate Plugin - How to create a simple custom rule?
(8 answers)
Closed 8 years ago.
I am trying to override the functionality of 'required' and 'minlength' validators provided by jQuery Validator plugin. Following is my code:
jQuery(".form-cls").validate({
errorElement: "span",
errorClass: "error-msg",
rules: {
email:{
required:true,
email:true,
alreadyExistEmail: true
},
user_email:{
required:true,
email:true
},
password:{
required: function (element) {
return checkIfPasswordIsRequired();
},
minlength: function (element) {
return passwordLengthCheck(element);
}
}
}
});
function checkIfPasswordIsRequired()
{
if(jQuery("#facebook_id").length > 0 || jQuery("#linkedin_id").length > 0 || jQuery("#xing_id").length > 0) {
if(jQuery("#facebook_id").val() != "" || jQuery("#linkedin_id").val() != "" || jQuery("#xing_id").val() != "") {
return false;
}
}
return true;
}
function passwordLengthCheck(element)
{
if(element.value.length < 6)
{
return false;
}
else
{
return true;
}
}
Now here the first check on password field is working fine that is of 'required' but the second check is not working. If have checked it my console.log(element.value.length) and it gives the length of value in password field each time the value is changed/altered but depending on the condition in function passwordLengthCheck it never shows/displays error. Kindly help!!
To check field length, just add minlength attribute to the <input> tag, like
<input id="pw" name="pw" minlength="6" type="password">
To add some custom validation, use addMethod():
jQuery.validator.addMethod("func", function(value, element) {
return this.optional(element) || check expression here;
}
The following code checks to see if user entered their email address & password.
This works great in Firefox/Chrome, but I'm having issues in IE. In IE, when you don't enter your user name and password, it turns the boxes red (as expected), and also doesn't change the text to 'Loading...', so it goes to the return false in the last else statement, but IE doesn't recognize it for some reason—is there a work around I should know about?
$('#gettheiremail').submit( function() {
var passwordinfo = $('#passwordtextbox').val();
if ($('#signuptextbox').attr('value') == '' || $('#signuptextbox').attr('value') == 'Your Email Address' ) {
$('#signuptextbox').css('color','red');
$('#signuptextbox').css('border','3px solid #ff0000');
}
if ($('#passwordtextbox').attr('value') == '') {
$('#fakepassword').css('color','red');
$('#fakepassword').css('border','3px solid #ff0000');
$('#passwordtextbox').css('border','3px solid #ff00000');
}
if((!($('#signuptextbox').attr('value') == '' || $('#signuptextbox').attr('value') == 'Your Email Address' )) && $('#passwordtextbox').attr('value') != '' )
{
$('#sendform').val('Loading...');
}
else
{
return false;
}
});
Here's the working code after fixes
$('#gettheiremail').submit( function(e) {
var signuptextbox = $('#signuptextbox').attr('value');
if (signuptextbox == 'Your Email Address' ) {
$('#signuptextbox').css('color','red');
$('#signuptextbox').css('border','3px solid #ff0000');
e.preventDefault();
}
var passwordtextbox = $('#passwordtextbox').attr('value');
if (passwordtextbox == '' || passwordtextbox == 'Enter Your Email Password') {
$('#fakepassword').css('color','red');
$('#fakepassword').css('border','3px solid #ff0000');
$('#passwordtextbox').css('border','3px solid #ff00000');
e.preventDefault();
}
if(!((passwordtextbox == '' || passwordtextbox == 'Enter Your Email Password') && (signuptextbox == 'Your Email Address')))
{
$('#sendform').val('Loading...');
}
});
Have you tried:
$('#gettheiremail').submit(function(e) {
/* Other code */
e.preventDefault();
/* Other code */
});
If neither of the two things at the end of your code are happening, then the odds are that you're never reaching the end of that code — e.g., that an exception is being thrown in the middle. You'll want to walk through with a debugger (you can use the built-in stuff in IE8+, or VS.Net [there's a free edition] for earlier versions).
Off-topic: You're reiterating a lot of lookups:
if ($('#signuptextbox').attr('value') == '' || $('#signuptextbox').attr('value') == ' ...
Every time you write $('#xyz') it triggers several function calls, at least one memory allocation, and causes a DOM lookup (which is not necessarily all that fast, even when looking up by id). Similarly, constantly calling attr again for the same attribute is more unnecessary overhead (though not nearly so much). Instead:
var signuptextbox = $('#signuptextbox'),
signupvalue = signupvalue = signuptextbox.attr('value');
if (signupvalue == '' || signupvalue == '...
(Or don't keep the signuptextbox if you just need its value.)