Related
I have an Ant Design form with validation <a-from-model> for multiple inputs:
<a-form-model
ref="ruleForm"
:model="form"
:rules="rules"
:label-col="labelCol"
:wrapper-col="wrapperCol"
>
<a-form-model-item has-feedback label="Git Clone Address(.git)" prop="git_address">
<a-input v-model="form.git_address" />
</a-form-model-item>
<a-form-model-item has-feedback label="Number of Modules" prop="module_number">
<a-form-model-item label="Notification Lists(seperated by ,)" prop="maillist">
<a-input v-model="form.maillist" placeholder="johndoe#gmail.com" />
</a-form-model-item>
<a-form-model-item has-feedback label="CI/CD Stages" prop="stage">
<a-checkbox-group v-model="form.stage">
<a-checkbox value="1" name="stage"> Stage 1 </a-checkbox>
<a-checkbox value="2" name="stage"> Stage 2 </a-checkbox>
<a-checkbox value="3" name="stage"> Stage 3 </a-checkbox>
</a-checkbox-group>
</a-form-model-item>
<a-form-model-item :wrapper-col="{ span: 14, offset: 4 }">
<a-button type="primary" #click="onSubmit"> Create </a-button>
<a-button style="margin-left: 10px" #click="onCancel"> Cancel </a-button>
</a-form-model-item>
</a-form-model>
Now to submit the form, I need all the validation to pass. This is what export default {data() { looks like:
data() {
let checkPending;
let checkModuleNumber = (rule, value, callback) => {
clearTimeout(checkPending);
if (!value) {
return callback(new Error('Please input the number'));
}
checkPending = setTimeout(() => {
if (!Number.isInteger(value)) {
callback(new Error('Please input digits'));
} else {
if (value < 1) {
callback(new Error('Module number must at least be 1!'));
} else {
callback();
}
}
}, 1000);
};
let checkGitAddress = (rule, value, callback) => {
clearTimeout(checkPending);
checkPending = setTimeout(() => {
if (!value.endsWith(".git")) {
callback(new Error("Please enter clone address that ends with .git!"));
} else {
callback();
}
}, 1000);
};
let checkModuleStage = (rule, value, callback) => {
clearTimeout(checkPending);
checkPending = setTimeout(() => {
if (value.some((x) => x === "2") && value.some((x) => x === "3")) {
callback(
new Error(
"You have selected both Stage 1 and Stage 2. Only one can be selected:"
)
);
} else {
callback();
}
}, 1000);
};
This is what rules looks like:
rules: {
git_address: [
{ required: true, message: "Please input git address", trigger: "change" },
{ validator: checkGitAddress, trigger: "change" },
],
module_number: [
{ required: true, message: "Please input module number", trigger: "change" },
{ validator: checkModuleNumber, trigger: "change" },
],
maillist: [
{ required: true, message: "Please input maillist", trigger: "change" },
],
stage: [
{
type: 'array',
required: true,
message: 'Please select at least one stage',
trigger: 'change',
},
{ validator: checkModuleStage, trigger: "change" },
],
},
Inside methods I defined the following two functions:
onSubmit() {
this.$refs.ruleForm.validate(valid => {
if (valid) {
this.display = true;
alert('submit!');
} else {
console.log('error submit!!');
return false;
}
});
},
onCancel() {
this.$refs.ruleForm.resetFields();
},
The problem I have right now is, whenever I changed the input the validation completes really fast. But when I click the submit button, all checks except the last one stuck at the checkPending state, thus the validation can never be completed. Does anyone know what results in this?Thanks so much!
(My wild guess is because all the check function is async but not sure if it's the case and don't know how to resolve it :(()
Turns out all I need to do is to remove all the checkpending functions and only keep the error handeling. async function wound't work in this scenario.
clearTimeout(checkPending);
checkPending = setTimeout(() => {
if ("some condition") {
callback(
new Error("some error")
);
} else {
callback();
}
}, 1000);
I'm having an issue with JQuery Validation inside a modal with tabs.
On the Sign in Tab, when I click on the Login button the validation errors pop up as intended:
PROBLEM 1
But on the New Account tab, when I click on the Get Started Button. Nothing Happens. The Submit Handler is not triggering, and the validation error messages only appear if I type something.
PROBLEM 2
When I click on the Forgot Password Link, it takes me to another tab in the same modal. But when I press the "Reset Password" Button I get another error:
{"error":"key missing: title"}
What am I doing wrong? Is it possible to have 3 submit handlers working properly for the three different tabs in the modal?
Use this fiddle to troubleshoot it:
https://jsfiddle.net/h6onufs5/
JS:
(function(){
//Login/Signup modal window - by CodyHouse.co
function ModalSignin( element ) {
this.element = element;
this.blocks = this.element.getElementsByClassName('js-signin-modal-block');
this.switchers = this.element.getElementsByClassName('js-signin-modal-switcher')[0].getElementsByTagName('a');
this.triggers = document.getElementsByClassName('js-signin-modal-trigger');
this.hidePassword = this.element.getElementsByClassName('js-hide-password');
this.init();
};
ModalSignin.prototype.init = function() {
var self = this;
//open modal/switch form
for(var i =0; i < this.triggers.length; i++) {
(function(i){
self.triggers[i].addEventListener('click', function(event){
if( event.target.hasAttribute('data-signin') ) {
event.preventDefault();
self.showSigninForm(event.target.getAttribute('data-signin'));
}
});
})(i);
}
//close modal
this.element.addEventListener('click', function(event){
if( hasClass(event.target, 'js-signin-modal') || hasClass(event.target, 'js-close') ) {
event.preventDefault();
removeClass(self.element, 'cd-signin-modal--is-visible');
}
});
//close modal when clicking the esc keyboard button
document.addEventListener('keydown', function(event){
(event.which=='27') && removeClass(self.element, 'cd-signin-modal--is-visible');
});
//hide/show password
for(var i =0; i < this.hidePassword.length; i++) {
(function(i){
self.hidePassword[i].addEventListener('click', function(event){
self.togglePassword(self.hidePassword[i]);
});
})(i);
}
//IMPORTANT - REMOVE THIS - it's just to show/hide error messages in the demo
this.blocks[0].getElementsByTagName('form')[0].addEventListener('submit', function(event){
event.preventDefault();
self.toggleError(document.getElementById('signin-email'), true);
});
this.blocks[1].getElementsByTagName('form')[0].addEventListener('submit', function(event){
event.preventDefault();
self.toggleError(document.getElementById('signup-username'), true);
});
};
ModalSignin.prototype.togglePassword = function(target) {
var password = target.previousElementSibling;
( 'password' == password.getAttribute('type') ) ? password.setAttribute('type', 'text') : password.setAttribute('type', 'password');
target.textContent = ( 'Hide' == target.textContent ) ? 'Show' : 'Hide';
putCursorAtEnd(password);
}
ModalSignin.prototype.showSigninForm = function(type) {
// show modal if not visible
!hasClass(this.element, 'cd-signin-modal--is-visible') && addClass(this.element, 'cd-signin-modal--is-visible');
// show selected form
for( var i=0; i < this.blocks.length; i++ ) {
this.blocks[i].getAttribute('data-type') == type ? addClass(this.blocks[i], 'cd-signin-modal__block--is-selected') : removeClass(this.blocks[i], 'cd-signin-modal__block--is-selected');
}
//update switcher appearance
var switcherType = (type == 'signup') ? 'signup' : 'login';
for( var i=0; i < this.switchers.length; i++ ) {
this.switchers[i].getAttribute('data-type') == switcherType ? addClass(this.switchers[i], 'cd-selected') : removeClass(this.switchers[i], 'cd-selected');
}
};
ModalSignin.prototype.toggleError = function(input, bool) {
// used to show error messages in the form
toggleClass(input, 'cd-signin-modal__input--has-error', bool);
toggleClass(input.nextElementSibling, 'cd-signin-modal__error--is-visible', bool);
}
var signinModal = document.getElementsByClassName("js-signin-modal")[0];
if( signinModal ) {
new ModalSignin(signinModal);
}
// toggle main navigation on mobile
var mainNav = document.getElementsByClassName('js-main-nav')[0];
if(mainNav) {
mainNav.addEventListener('click', function(event){
if( hasClass(event.target, 'js-main-nav') ){
var navList = mainNav.getElementsByTagName('ul')[0];
toggleClass(navList, 'cd-main-nav__list--is-visible', !hasClass(navList, 'cd-main-nav__list--is-visible'));
}
});
}
//class manipulations - needed if classList is not supported
function hasClass(el, className) {
if (el.classList) return el.classList.contains(className);
else return !!el.className.match(new RegExp('(\\s|^)' + className + '(\\s|$)'));
}
function addClass(el, className) {
var classList = className.split(' ');
if (el.classList) el.classList.add(classList[0]);
else if (!hasClass(el, classList[0])) el.className += " " + classList[0];
if (classList.length > 1) addClass(el, classList.slice(1).join(' '));
}
function removeClass(el, className) {
var classList = className.split(' ');
if (el.classList) el.classList.remove(classList[0]);
else if(hasClass(el, classList[0])) {
var reg = new RegExp('(\\s|^)' + classList[0] + '(\\s|$)');
el.className=el.className.replace(reg, ' ');
}
if (classList.length > 1) removeClass(el, classList.slice(1).join(' '));
}
function toggleClass(el, className, bool) {
if(bool) addClass(el, className);
else removeClass(el, className);
}
//credits http://css-tricks.com/snippets/jquery/move-cursor-to-end-of-textarea-or-input/
function putCursorAtEnd(el) {
if (el.setSelectionRange) {
var len = el.value.length * 2;
el.focus();
el.setSelectionRange(len, len);
} else {
el.value = el.value;
}
};
})();
$('#login-form').validate ({
// validation rules for registration form
errorClass: "error-class",
validClass: "valid-class",
errorElement: 'div',
errorPlacement: function(error, element) {
if(element.parent('.input-group').length) {
error.insertAfter(element.parent());
} else {
error.insertAfter(element);
}
},
onError : function(){
$('.input-group.error-class').find('.help-block.form-error').each(function() {
$(this).closest('.form-control').addClass('error-class').append($(this));
});
},
rules: {
username: {
required: true,
minlength: 3,
maxlength: 40
},
password: {
required: true,
minlength: 7,
maxlength: 20
}
},
messages: {
username: {
required: "Please enter your username or email address",
minlength: "Usernames can't be shorter than three characters",
maxlength: "Usernames or emails must be shorter than forty characters."
},
password: {
required: "Please enter your password",
minlength: "Passwords can't be shorter than seven characters",
maxlength: "Passwords must be shorter than forty characters."
},
highlight: function(element, errorClass) {
$(element).removeClass(errorClass);
}
},
submitHandler: function() {
$('#noenter').show();
}
});
$("#signup-form").validate ({
// validation rules for registration formd
errorClass: "error-class",
validClass: "valid-class",
errorElement: 'div',
errorPlacement: function(error, element) {
if(element.parent('.input-group').length) {
error.insertAfter(element.parent());
} else {
error.insertAfter(element);
}
},
onError : function(){
$('.input-group.error-class').find('.help-block.form-error').each(function() {
$(this).closest('.form-group').addClass('error-class').append($(this));
});
},
messages: {
email: {
required: "Please enter your email address",
},
highlight: function(element, errorClass) {
$(element).removeClass(errorClass);
}
}
});
$("#password-form").validate ({
// validation rules for registration formd
errorClass: "error-class",
validClass: "valid-class",
errorElement: 'div',
errorPlacement: function(error, element) {
if(element.parent('.input-group').length) {
error.insertAfter(element.parent());
} else {
error.insertAfter(element);
}
},
onError : function(){
$('.input-group.error-class').find('.help-block.form-error').each(function() {
$(this).closest('.form-group').addClass('error-class').append($(this));
});
},
messages: {
email: {
required: "Please enter your email address",
},
highlight: function(element, errorClass) {
$(element).removeClass(errorClass);
}
}
});
FOR HTML CLICK ON THE FIDDLE. Please let me know if it is possible to fix these problem. Thank you..
In the validator for the signup-form, you should add the rules for the email input like:
rules: {
email: {email:true, required:true}
}
I added it in this fiddle and it works: https://jsfiddle.net/h6onufs5/4/
Hope this helps
I am trying to create a validator which makes a REST call to my server and grabs a value the database. A few problems, when my validator is enabled it only validates that input and not the rest of the constraints. Also, I keep getting this error for the Id length [validate.js] Attribute id has a non numeric value for length, I do not receive this error when I am not using the async validator.
Here is my validator:
validate.validators.myAsyncValidator = function(input, options, key, attributes) {
return new validate.Promise(function(resolve, reject) {
if (!validate.isEmpty(input.value)) {
axios.get('/data-management/verify-data', {
params: {
id: input.value,
filter: options[0]
}
})
.then(function(response) {
if (response.data !== options[1]) resolve(" already exists!");
})
.catch(function(error) {
resolve(": Error, try again.");
});
}
}); };
Here are my constraints:
var constraints = {
email: {
presence: true,
email: true
},
password: {
presence: true,
format: {
// We don't allow anything that a-z and 0-9
pattern: "^[a-zA-Z0-9!##$&()\\-`.+,/\"]*$",
// but we don't care if the username is uppercase or lowercase
flags: "i",
message: "Must contain at least 1 Uppercase, 1 Lowercase, 1 Number, and 1 Special Character"
},
length: {
minimum: 6,
message: "Must be at least 6 characters"
}
},
"confirm-password": {
presence: true,
equality: {
attribute: "password",
message: "^The passwords does not match"
}
},
district: {
presence: true
},
id: {
presence: true,
length: {
minimum: 5,
maximum: 20,
message: "Must be between 6-20 characters"
},
format: {
// We don't allow anything that a-z and 0-9
pattern: "[a-z0-9]+",
// but we don't care if the username is uppercase or lowercase
flags: "i",
message: "can only contain a-z and 0-9"
},
myAsyncValidator: ["signup", false]
}};
And me hooking up my constraints to my form:
var inputs = document.querySelectorAll("input, textarea, select");
for (var i = 0; i < inputs.length; ++i) {
inputs.item(i).addEventListener("change", function(ev) {
// var errors = validate.async(form, constraints).then(function(data) {
// console.log("data");
// });
var obj = this;
var n = this.name;
validate.async(form, constraints).then(function() {
}, function(errors) {
showErrorsForInput(obj, errors[n.valueOf()]);
});
});
}
function handleFormSubmit(form, input) {
// validate the form against the constraints
// var errors = validate.async(form, constraints).then(function(data) {
// console.log("data2");
// });
validate.async(form, constraints).then(function() {
}, function(errors) {
showErrors(form, errors || {});
if (!errors) {
showSuccess();
}
});
I can provide the functons showErrors(), showSuccess(), and showErrorsForInput() if needed.
Thanks!
Found a solution. Checked the ID constraints first, once they were gone, I checked for rest of the constraints. Also added a tokenizer to remove the length error I was receiving.
Here is the updated code:
validate.validators.checkExists = function(input, options) {
return new validate.Promise(function(resolve, reject) {
if (!validate.isEmpty(input.value)) {
axios.get('/data-management/verify-data', {
params: {
id: input.value,
filter: options[0]
}
})
.then(function(response) {
if (response.data !== options[1]) resolve("already exists!");
else resolve();
})
.catch(function(error) {
reject(": Error, try again.");
});
} else resolve();
});
};
// These are the constraints used to validate the form
var constraints = {
email: {
presence: true,
email: true
},
password: {
presence: true,
format: {
pattern: "^[a-zA-Z0-9!##$&()\\-`.+,/\"]*$",
flags: "i",
message: "Must contain at least 1 Uppercase, 1 Lowercase, 1 Number, and 1 Special Character"
},
length: {
minimum: 6,
message: "must be at least 6 characters"
}
},
"confirm-password": {
presence: true,
equality: {
attribute: "password",
message: "^The passwords does not match"
}
},
firstName: {
presence: true
},
lastName: {
presence: true
},
district: {
presence: {
message: "must be selected"
}
}
};
var idConstraints = {
id: {
presence: true,
length: {
minimum: 5,
tokenizer: function(input) {
try {
return input.value;
} catch (e) {
return " ";
}
}
},
checkExists: ["signup", false]
}
};
// Hook up the form so we can prevent it from being posted
var form = document.querySelector("form#signup");
form.addEventListener("submit", function(ev) {
ev.preventDefault();
handleFormSubmit(form);
});
// Hook up the inputs to validate on the fly
var inputs = document.querySelectorAll("input, textarea, select");
for (var i = 0; i < inputs.length; ++i) {
inputs.item(i).addEventListener("change", function(ev) {
var obj = this;
var n = this.name;
validate.async(form, idConstraints).then(function() {
var moreErrors = validate(form, constraints) || {};
showErrorsForInput(obj, moreErrors[n.valueOf()]);
}, function(errors) {
showErrorsForInput(obj, errors[n.valueOf()]);
});
});
}
function handleFormSubmit(form) {
validate.async(form, idConstraints).then(function() {
var errors = validate(form, constraints);
showErrors(form, errors || {});
}, function(errors) {
showErrors(form, errors || {});
if (!errors) {
showSuccess();
}
});
}
I have one field which can contain email or mobile (in my case mobile is 8 digits).
I already tried two approaches (both examples doesn't work, because 'element' do not have validate method):
First approach: create custom method and do both validations there, but then I have to create my own email and mobile validation - I couldn't find a way how to reuse jQuery validation rules in new methods. This is what I'd like to have:
jQuery.validator.addMethod("mobileoremail", function(value, element) {
return this.optional(element) ||
element.validate({ rules: { digits: true, rangelength: [8, 8] } }) ||
element.validate({ rules: { email: true } });
}, "Invalid mobile or email");
Second approach: create dependent rules. And also in this case I couldn't find a way how to reuse jQuery validation rules.
{ myRules: {
rules: {
user: {
required: true,
email: {
depends: function(element) {
return !element.validate({ rules: { mobile: true } });
}
},
mobile: {
depends: function (element) {
return !element.validate({ rules: { email: true } });
}
}
}
}
}
}
How about the following validation method...
$.validator.addMethod("xor", function(val, el, param) {
var valid = false;
// loop through sets of nested rules
for(var i = 0; i < param.length; ++i) {
var setResult = true;
// loop through nested rules in the set
for(var x in param[i]) {
var result = $.validator.methods[x].call(this, val, el, param[i][x]);
// If the input breaks one rule in a set we stop and move
// to the next set...
if(!result) {
setResult = false;
break;
}
}
// If the value passes for one set we stop with a true result
if(setResult == true) {
valid = true;
break;
}
}
// Return the validation result
return this.optional(el) || valid;
}, "The value entered is invalid");
Then we could set up the form validation as follows...
$("form").validate({
rules: {
input: {
xor: [{
digits: true,
rangelength: [8, 8]
}, {
email: true
}]
}
},
messages: {
input: {
xor: "Please enter a valid email or phone number"
}
}
});
See it in action here http://jsfiddle.net/eJdBa/
I am using formValidation.io and need to dynamically add a callback type validator within a class so that it can use a class property. The issue is that I initially pass my validator options into a super call that has some form validation procedures. But this means I do not have initial access to class properties.
So to do this I was trying to use updateOption but it definitely does not begin to validate this.
class MyForm extends Form {
var validatorOptions = {
fields: {
phoneNumber: {
validators: {
regexp: {
regexp: Regexp.phone,
message: "Please enter a valid phone number"
}
}
}
}
};
super({
validator: {
options: validatorOptions
}
});
var self = this;
this._cachedPhoneNumbers = [];
var phoneValidatorCallback = {
message: "This number is already in use",
callback: function(value, validator, $field) {
if ($.inArray(value, self._cachedPhoneNumbers) > -1)
return false;
return true;
}
}
// ref to validator is definitely valid!
this.validator.updateOption('phone', 'callback', 'callback', phoneValidatorCallback);
}
Here is the answer. I simply misused the function.
class MyForm extends Form {
var validatorOptions = {
fields: {
phoneNumber: {
validators: {
regexp: {
regexp: Regexp.phone,
message: "Please enter a valid phone number"
},
callback: {
message: 'This number is in use',
callback: function() {
return true;
}
}
}
}
}
};
super({
validator: {
options: validatorOptions
}
});
var self = this;
this._cachedPhoneNumbers = [];
function phoneValidatorCallback(value, validator, $field) {
if ($.inArray(value, self._cachedPhoneNumbers) > -1)
return false;
return true;
}
// ref to validator is definitely valid!
this.validator.updateOption('phone', 'callback', 'callback', phoneValidatorCallback);
}