Vuejs form first input value always fails to validate - javascript

I have a parent and child form. Submit button is on parent, if there are invalid inputs from child, I would like to disable the Submit button. Example zip code must consist of only 5 numbers. 12345c would be invalid, The form takes 12345c but doesnt get it as invalid, however on all next invalid inputs it can detect them, 12345cc, 12345ccc. Also when I delete the invalid characters one by one it continues detecting the invalid arguments but fails to recognize first valid argument, when I delete 1 more character it sees it as valid(although invalid)
In Short: the validation is correct but always 1 step behind after any input change.
Here is my code:
updateFieldValue: function (data) {
this.formData[data.key].value = data.value;
this.requiredFieldsDone = this.allRequiredFieldsFilled();
this.formWasUpdated();
this.$emit('general-form-data-updated')
},
formWasUpdated: function () {
let result = false;
for (const [key, formField] of Object.entries(this.formData)) {
if (formField.value !== formField.initialValue) {
result = true;
}
}
this.areFieldsValid = this.fullFormValidated();
console.log('Child valid');
console.log(this.areFieldsValid);
this.hasChanged = result;
this.$emit(
'general-edit-form-has-changed',
this.hasChanged,
this.areFieldsValid
);
},
fullFormValidated: function () {
let validated = true;
console.log('validateForm for loop');
for (let inputElement in this.$refs) {
let errors = this.$refs[inputElement].validateInput();
if (errors) {
validated = false;
}
}
console.log('validated boolean');
console.log(validated);
return validated;
},
validateInput: function() {
this.form.validateFields(this.validateInputCallback);
return this.validateErrors;
},
validateInputCallback: function(errors, values ) {
if ( errors ) {
this.validateErrors = errors;
} else {
this.validateErrors = false;
}
},
Console Output:
validateForm for loop
address-edit-form.vue:613 validated boolean
address-edit-form.vue:614 true
address-edit-form.vue:506 Child valid
address-edit-form.vue:507 true
address-edit-modal.vue:124 Parent valid
address-edit-modal.vue:125 true
index.js:120 async-validator: ['field.value value 12345c does not match pattern /^[0-9]{5,5}$/']
Im using ant Forms: https://ant.design/components/form
I would appreciate any help!

Related

condense if, else JS with similar condition rules

trying to find a way to condense this. wasnt sure of the best way to do it. basically if criteria is met i display an alert with a parameter that is the message. i was thinking of maybe trying it in function. this is part of a larger function react component. i was also thinking if i could find a way to condense the else if's i could use a ternary. thanks in advance for the assistance.
const handleUpdatePassword = () => {
const allFilled = !reject(passwords).length;
const passwordsMatch = newPassword === conPassword;
const isDifferent = curPassword !== newPassword;
const meetsPasswordRequirements = validatePassword();
const usesName = isUsingName();
const usesUserID = isPartOfUserID();
const isValidPassword = meetsPasswordRequirements && isDifferent;
if (allFilled) {
if (!isDifferent) {
Alert.alert(difPassWord);
} else if (!passwordsMatch) {
Alert.alert(noMatch);
} else if (!meetsPasswordRequirements) {
Alert.alert(pasReqs);
} else if (usesName || usesUserID) {
Alert.alert(pasName);
}
} else {
Alert.alert(fieldNotComplete);
}
if (isValidPassword) {
changePasswordPost(
{
userId,
curPassword,
newPassword
},
partyId
);
}
};
You can create an array of objects for your validation rules, each containing a function which returns a boolean indicating whether that validation passes, and a string with the error message to display.
Then loop over the rules array and alert the message for the first rule that returns false. If they all return true, do the post.
You can split each if statement into a function, then chain them. For example
// here we make a closure to validate, and return a Promise
// condition can be a function
const validate = (condition, error) => ()=> new Promise((res, rej)=>{
if(condition()){
res();
}else{
rej(error);
}
});
const handleUpdatePassword = () => {
const validateFieldsComplete = validate(
()=>!reject(passwords).length,
fieldNotComplete
);
const validateDifPassword = validate(
()=> curPassword !== newPassword,
difPassWord
);
// ...
validateFieldsComplete()
.then(validateDifPassword)
.then(...)
.catch(Alert.alert)
}
It would be much cleaner with pipe. You can take a look at ramda. Or if you are intrested in functional way, you might consider using Monad.
I'd recommend DRYing up the Alert.alert part since all branches have that in common, and just come up with an expression that evaluates to the alert message. Compactness isn't always everything, but if you want it, then nested conditional operators can fit the bill. I'm also rearranging your conditions so that it can be a flat chain of if/elses:
const message
= reject(passwords).length ? fieldNotComplete
: curPassword === newPassword ? difPassWord
: newPassword !== conPassword ? noMatch
: !validatePassword() ? pasReqs
: (isUsingName() || isPartOfUserID()) ? pasName
: null;
const isValid = !message;
if (!isValid) {
Alert.alert(message);
}
(feel free to use any other sort of code formatting pattern; nested conditionals always look awkward no matter which pattern you use, IMO.)
Edit:
Also inlined conditionals which will short-circuit evaluation and make it even more compact.
I'd setup a validations object that has the tests and error messages and then loop over it. If validation fails, it'll throw the last validation error message. Using this method, you only have to maintain your tests in one place and not mess with a block of conditional statements.
const handleUpdatePassword = () => {
const validations = {
allFilled: {
test() {
return newPass && oldPass
},
error: 'Must fill out all fields'
},
correct: {
test() {
return curPass === oldPass
},
error: 'Incorrect password'
},
[...]
}
const invalid = () => {
let flag = false
for (let validation in validations) {
if (!validations[validation].test()) {
flag = validations[validation].error
}
}
return flag
}
if (invalid()) {
Alert.alert(invalid())
} else {
changePasswordPost(
{
userId,
curPass,
newPass
},
partyId
)
}
}
hi everyone this was the method i used for a solution
const messages = [
{
alertMessage: difPassWord,
displayRule: different()
},
{
alertMessage: noMatch,
displayRule: match()
},
{
alertMessage: pasReqs,
displayRule: validatePassword()
},
{
alertMessage: pasName,
displayRule: !isUsingName() || !isPartOfUserID()
}
];
if (allFilled) {
const arrayLength = messages.length;
for (let i = 0; i < arrayLength; i++) {
if (messages[i].displayRule === false) {
Alert.alert(messages[i].alertMessage);
}
}

Improving readability of multiple if statements with common pattern

I am doing some JavaScript front-end and I have a heavy load of forms, all of which need validation. As of now I am using this structure :
function validateForm() {
let form = document.forms["form-add-consumer"];
let id = form["input-id"].value;
let lastName = form["input-last-name"].value;
let firstName = form["input-first-name"].value;
...
let missing = false;
if (lastName.trim() === "") {
document.getElementById("input-last-name-error").className = "error";
missing = true;
}
if (firstName.trim() === "") {
document.getElementById("input-first-name-error").className = "error";
missing = true;
}
if(missing){
return false
} else {
return buildRequest(id, firstName, lastName, ...);
}
}
As you can see, for large forms the function will quickly grow. The code is a bit redundant for each field:
Declare form field
Check its value against a boolean condition
If boolean failed, display the error label and set the failed boolean to true to not send the request
How could I improve this code without complexyfing it too much (no library if possible) ?
Perhaps you could create an object that contians per-field validators, with selectors for respective fields, so that you can perform the nessisary validation in a more concise way like so:
function validateForm() {
let form = document.forms["form-add-consumer"];
let id = form["input-id"].value;
let lastName = form["input-last-name"].value;
let firstName = form["input-first-name"].value;
...
// Construct an object with selectors for the fields as keys, and
// per-field validation functions as values like so
const fieldsToValidate = {
'#input-id' : value => value.trim() !== '',
'#input-last-name' : value => value.trim() !== '',
'#input-first-name' : value => value.trim() !== '',
...,
'#number-field' : value => parseInt(value) > 0, // Different logic for number field
...
}
const invalidFields = Object.entries(fieldsToValidate)
.filter(entry => {
// Extract field selector and validator for this field
const fieldSelector = entry[0];
const fieldValueValidator = entry[1];
const field = form.querySelector(fieldSelector);
if(!fieldValueValidator(field.value)) {
// For invalid field, apply the error class
field.className = 'error'
return true;
}
return false;
});
// If invalid field length is greater than zero, this signifies
// a form state that failed validation
if(invalidFields.length > 0){
return false
} else {
return buildRequest(id, firstName, lastName, ...);
}
}

Validate forms using javascript

I want to validate 3 inputs (name, email and password) in a form using javascript. When the user submits the form, and all the fields are empty, it works correctly showing the error messages. But then if I write a correct password (length 7) and wrong email and name, and I try to submit the form again the "Password too short" message is stil there and the password is correct. What I am doing wrong?
Javascript file
function verify(){
if(verName()&verEmail()&verPassword())
{
return true;
}else
{
verName();
verEmail();
verPassword();
return false;
}
}
function verPassword(){
var ok = true;
var frm = document.getElementById("register");
var pass = frm.elements[2].value;
if(pass.length<6)
{
var text="Password too short";
document.getElementById('textPassword').innerHTML=text;
ok = false;
}
return ok;
}
HTML file
<form id='register' name='register' onsubmit="return verify()">
function verify(){
document.getElementById('textPassword').innerHTML = ' ';
if(verName()&verEmail()&verPassword())
{
return true;
}else
{
verName();
verEmail();
verPassword();
return false;
}
}
change your code it like this:
function verify(){
if(verName()&verEmail()&verPassword())
{
return true;
}
else
{
if(verName());
if(verEmail());
if(verPassword());
return false;
}
}
with this solution, each validation occurs if the previous validation runs true! and if not, just the previous validation errors shows up !
in each function verName(), verEmail() and verPassword(), return Boolean value of TRUE of FALSE
also add this line of code, on your form submit event:
verify() {
document.getElementById('textPassword').innerHTML= ' '
....
....
}
The problem is that your verPassword function is adding that error string when the password is invalid, but it doesn't remove it when the password is valid.
Also, your verify function makes little sense.
How about:
function verify(){
return verName() && verEmail() && verPassword();
}
function verPassword(){
var frm = document.getElementById("register");
var pass = frm.elements[2].value;
var ok = pass.length > 5;
var text = ok ? "" : "Password too short";
document.getElementById('textPassword').innerHTML=text;
return ok;
}
You have to empty the #textPassword element by write something like: document.getElementById('textPassword').innerHTML.
In addition I can see some wrong codes there. First, if every ver* function returns true or false, you better use && rather than & in if condition expression. Or you can just return the evaluated value of the condition expression like this: return verName() && verEmail() && verPassword().
Second, the ver* functions are already called while if evaluate condition expression. No need to call those functions again in else part.
And I don't think you need ok variable in verPassword() function.
I suggest to change the code like below:
function verify(){
return verName() && verEmail() && verPassword();
}
function verPassword(){
var frm = document.getElementById("register");
var pass = frm.elements[2].value;
var textPassword = document.getElementById('textPassword');
if (pass.length < 6) {
var text="Password too short";
textPassword.innerHTML = text;
return false;
} else {
textPassword.innerHTML = ""; // Empty #textPassword
return true;
}
}

prevent jQuery form submit if validation fail

Hi I'm trying to prevent this AJAX submit form function when a validation fails on one of one of the various inputs.
Edit: What I want to know exactly is what I need to include in //Adult age validation and var handleFormSubmit to stop saveForm($('#quotecriteria_form')); and performAjaxSubmit(formData, url); from occurring.
Submit function:
var handleFormSubmit = function( e ) {
e.preventDefault();
var $submit = $('button[type="submit"]');
$submit.addClass('loading');
// Save the contents of the form to session cookie
saveForm($('#quotecriteria_form'));
var formData = $('#quotecriteria_form').serialize();
var url = $('#quotecriteria_form').attr('action');
performAjaxSubmit(formData, url);
}
validation functions initialized:
IndexPageFunctions = {
init: function() {
//Validate date range
this.validateToDate();
this.validateFromDate();
//Validate adult age
this.validateAdultAge0();
this.validateAdultAge1();
this.validateAdultAge2();
this.validateAdultAge3();
//Validate child age
this.validateChildAge0();
this.validateChildAge1();
this.validateChildAge2();
this.validateChildAge3();
this.validateChildAge4();
this.validateChildAge5();
this.validateChildAge6();
this.validateChildAge7();
this.validateChildAge8();
},
One of the many validation functions:
//Adult age validation
validateAdultAge0: function() {
$('button[type="submit"]').on('click', function() {
var inputVal = parseInt( $("input[name='adultAges[0]']").val());
if (inputVal < 18) {
$("input[name='adultAges[0]']").css("border-color","red");
$("div#under18").addClass('show').removeClass('hidden');
}
});
},
Define a boolean array outside the scope of all functions with an index for each input element:
var valid = [false,false,false...];
Set the original value to false if blank input should be considered invalid or true if the field is optional. In each of your validation handlers (like validateAdultAge0) change the corresponding flag accordingly. For example if you decide to map the validity of age0 to the 0th index then:
if (inputVal < 18) {
$("input[name='adultAges[0]']").css("border-color","red");
$("div#under18").addClass('show').removeClass('hidden');
valid[0] = false;
}else{
valid[0] = true;
}
Like someone in the comments pointed out, if the logic is the same for each of these functions you should be using an argument to specify the target input, not creating an entirely new function but I won't get into that here.
In handleFormSubmit you'll just need to check if all the flags in the valid array are set to true, if any one is false you can return:
for(var i=0;i<valid.length;i++){
if(!valid[i]){
return;
}
}

jQuery Validation with promises

I'm using the jQuery Validation plugin for a signup form and trying to check if an email address is already taken as part of the validation. The problem is that the check happens via a promise, and I don't know how (or if) I can make this work with the validation plugin. Here's what I have currently:
$("#signup form").validate({
rules: {
emailSignup: {
email: true,
required: true,
remote: checkAvailable
}
}
});
function checkAvailable() {
var email = $("#emailSignup").val(),
available;
App.isEmailAvailable(email)
.then(function(response) {
available = response;
})
.error(function() {
available = response;
});
setTimeout(function() {
if (available == true) {
console.log("is available");
return true;
} else {
console.log("not available");
return false;
}
}, 100);
}
The setTimeout is just a hacky way to ensure I have the response before I try to log it (I know that's terrible, but it's just for testing).
The thing is, this will correctly log is available or not available depending on the email address I enter, so it works up to that point. But I can't seem to actually return true/false back up to the remote method so that it kicks off the error handling. So what happens is anything that's a syntactically valid email gets marked as valid, even if it's logging not available based on the response.
Is this at all possible?
Update: I also tried doing this in a custom method:
jQuery.validator.addMethod("email", function(value, element) {
var field = this,
available;
App.isEmailAvailable(value)
.then(function(response) {
available = response;
})
.error(function() {
available = response;
});
setTimeout(function() {
if (available == true) {
console.log("is available");
return field.optional(element) || /^[\w-+\.]+#([\w-]+\.)+[\w-]{2,4}$/.test(value);
} else {
console.log("not available");
return false;
}
}, 100);
}, jQuery.validator.format("Please enter a valid email address."));
So the idea here is it would check if the address is available, and if it is, then it would check that it's syntactically valid. Again, it correctly logs is available/not available based on the address I enter, but doesn't return true/false properly to mark the field as valid or invalid.
Based on the "remote" jquery validate rule :
$.validator.addMethod('users_email_exists', function (value, element) {
var method = 'remote';
var previous = this.previousValue(element, method);
var validator = this;
if (!this.settings.messages[element.name]) {
this.settings.messages[element.name] = {};
}
previous.originalMessage = previous.originalMessage || this.settings.messages[element.name][method];
this.settings.messages[element.name][method] = previous.message;
var optionDataString = $.param({data: value});
if (previous.old === optionDataString) {
return previous.valid;
}
previous.old = optionDataString;
this.startRequest(element);
new Promise(function (fulfill) {
// YOUR STUFF, YOUR AJAX GET/POST REQUEST AND URL WITH PARAMS
$.get('/backend/users/ajax/filtered-users-list', {email: value})
.done(function (data) {
// YOUR STUFF TO VALIDATE DATA
// IF VALID TRUE -> validation success
// IF VALID FALSE -> validation failure
var valid = !data.length;
fulfill(valid);
})
}).then(function(valid) {
validator.settings.messages[ element.name ][ method ] = previous.originalMessage;
if ( valid ) {
submitted = validator.formSubmitted;
validator.resetInternals();
validator.toHide = validator.errorsFor( element );
validator.formSubmitted = submitted;
validator.successList.push( element );
validator.invalid[ element.name ] = false;
validator.showErrors();
} else {
errors = {};
message = validator.defaultMessage( element, { method: method, parameters: value } );
// YOUR STUFF, YOUR VALIDATION MESSAGE HERE
errors[ element.name ] = previous.message = 'EMAIL ALREADY ASSIGNED TO AN USER';
validator.invalid[ element.name ] = true;
validator.showErrors( errors );
}
previous.valid = valid;
validator.stopRequest( element, valid );
});
return "pending";
},
"EMAIL ALREADY ASSIGNED TO AN USER"
);
Then call your custom rule :
$("#signup form").validate({
rules: {
emailSignup: {
email: true,
required: true,
users_email_exists: true
}
}
});
Include this script as Promise class reference :
<!-- promise -->
<script src="https://www.promisejs.org/polyfills/promise-7.0.4.min.js"></script>
<script src="https://www.promisejs.org/polyfills/promise-done-7.0.4.min.js"></script>
The function that you've passed to setTimeout() will execute in future (a.k.a asynchronously) - after your checkAvailable() is completed. So its returning value is meaningless for the checkAvailable().
You should do rather the following:
DisableTheForm();
App.isEmailAvailable(value)
.then(function(response) {
$("#signup form").validate();
if( it is valid) {
EnableTheForm();
PostFormData();
}
})
.error(function() {
CryAsItIsNotAvailable();
EnableTheForm();
});
So to do your validation in response of positive feedback of your isEmailAvailable
You cannot use the remote method because it's looking for a URL parameter to access "remotely" via ajax().
You obviously would not call a JavaScript function with ajax(), so using remote to call a JavaScript function makes no sense.
You might be able to create a custom function using the .addMethod() method. However, you will have issues if any part of that is performed asynchronously as the custom rule will be evaluated before you have the result.

Categories

Resources