I would like test my Array (input value) before submit my form.
My array with value :
const fields = [
this.state.workshopSelected,
this.state.countrySelected,
this.state.productionTypeSelected,
this.state.numEmployeesSelected,
this.state.startAt
];
I've try this :
_.forEach(fields, (field) => {
if (field === null) {
return false;
}
});
alert('Can submit !');
...
I think my problem is because i don't use Promise. I've try to test with Promise.all(fields).then(());, but i'm always in then.
Anyone have idea ?
Thank you :)
The problem is that even though you're terminating the lodash _.forEach loop early, you don't do anything else with the information that you had a null entry.
Instead of lodash's _.forEach, I'd use the built-in Array#includes (fairly new) or Array#indexOf to find out if any of the entries is null:
if (fields.includes(null)) { // or if (fields.indexOf(null) != -1)
// At least one was null
} else {
// All were non-null
alert('Can submit !');
}
For more complex tests, you can use Array#some which lets you provide a callback for the test.
Live example with indexOf:
const state = {
workshopSelected: [],
countrySelected: [],
productionTypeSelected: [],
numEmployeesSelected: [],
startAt: []
};
const fields = [
state.workshopSelected,
state.countrySelected,
state.productionTypeSelected,
state.numEmployeesSelected,
state.startAt
];
if (fields.indexOf(null) != -1) {
console.log("Before: At least one was null");
} else {
console.log("Before: None were null");
}
fields[2] = null;
if (fields.indexOf(null) != -1) {
console.log("After: At least one was null");
} else {
console.log("After: None were null");
}
You do not need to use promises unless there is an asynchronous operation (for example if you are getting that array from your server).
If you already have that array you can do something like:
// Using lodash/underscore
var isValid = _.every(fields, (field) => (field!==null)}
// OR using the Array.every method
var isValid = fields.every((field)=>(field!==null))
// Or using vanilla JS only
function checkArray(array){
for(var i = 0; i < array.length ; i ++){
if(array[i]===null){
return false;
}
}
return true;
}
var isValid = checkArray(fields);
// After you get that value, you can execute your alert based on it
if(!isValid){
alert('Something went wrong..');
}
Try this simple snippet
var isAllowedToSubmit = true;
_.forEach(fields, (field) => {
if (!field) {
isAllowedToSubmit = false;
}
});
if(isAllowedToSubmit)
alert('Can submit !');
You can do that without library:
if (fields.some(field => field === null)) {
alert('Cannot submit');
} else {
alert('Can submit');
}
You don't need to use lodash, you can do this in simple vanilla javascript. Simply iterate over each field and if an error occurs set your errors bool to true
let errors = false;
fields.forEach(field) => {
if(field === null || field === '') {
errors = true;
}
});
if (!errors) {
alert('Yay no errors, now you can submit');
}
For an es6 you can use.
const hasNoError = fields.every((field, index, selfArray) => field !== null);
if (!hasNoError) {
alert('yay It works');
};
Have a look at Array.every documentation Array every MDN documentation
Related
JavaScript is known to only check the first variable in a && comparison in case the first variable returns false. Is there a way to 'ask' JavaScript to check both variables i.e. when they are methods?
For example: Suppose you have 2 methods that validate 2 separate user inputs:
const validateEmail = value => {
if(value.contains('#')){
setShowEmailError(false);
return true;
}
setShowEmailError(true);
return false;
};
const validatePswd = value => {
if(value !== ''){
setShowPswdError(false);
return true;
}
setShowPswdError(true);
return false;
};
Then check both conditions:
if(validateEmail(email) && validatePswd(pswd)){
//validate entire form and render errors
}
However, the above will not execute the validatePswd method if the first method validateEmail returns false.
Is there a way to check if both values are true and run both methods? Having JavaScript run both methods would be a breeze in some cases.
You can execute them in an array and then accumulate the result with && by reduce function.
const validateEmail = value => {
if(value.includes('#')){
//setShowEmailError(false);
return true;
}
//setShowEmailError(true);
console.log('wrong email')
return false;
};
const validatePswd = value => {
if(value !== ''){
//setShowPswdError(false);
return true;
}
// setShowPswdError(true);
console.log('wrong password');
return false;
};
// you can execute any number of validations within the array.
const result = [validateEmail('something'), validatePswd('')].reduce((acc, f) => acc && f, true);
console.log(result)
UPDATE
Or as #lux suggested using every method.
const validateEmail = value => {
if(value.includes('#')){
//setShowEmailError(false);
return true;
}
//setShowEmailError(true);
console.log('wrong email')
return false;
};
const validatePswd = value => {
if(value !== ''){
//setShowPswdError(false);
return true;
}
// setShowPswdError(true);
console.log('wrong password');
return false;
};
// you can execute any number of validations within the array.
const result = [validateEmail('something'), validatePswd('')].every(r => r);
console.log(result)
I don't know if you are looking for something like this:
const valEmail = validateEmail(email);
const valPsw = validatePswd(pswd);
if(valEmail && valPsw ){
//validate entire form and render errors
}
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);
}
}
i have a problem with a mongoDb query, i need to search for a parameter value of any MongoDB field.
I use a function in $where clause like:
db.response.find(
{
$where: function() {
var deepIterate = function (obj, value) {
for (var field in obj) {
if (obj[field] == value){
return true;
}
var found = false;
if ( typeof obj[field] === ‘object’) {
found = deepIterate(obj[field], value)
if (found) { return true; }
}
}
return false;
};
return deepIterate(this, “Text36")
}
});
The returned response was fine but I don't know how I can pass the value (Text36 in this sample) I want to found like a parameter
can someone help me please ? Thanks
I have some jQuery which uses an each loop to go through values entered in a repeated form field on a Symfony 3 CRM. There is a $.post which sends the entered value to a function that checks for duplicates in the database, and if it's a duplicate it adds something to an array, otherwise it adds a blank value to indicate it's not a dupe. Once these have been done, it then checks the final array and adds any errors to the error block to display to the user.
However, it seems that the array is ALWAYS blank and I belivee it's because it's running the code that displays the errors BEFORE it's actually finished getting the response.
Here is my code:
$('#puppy_form').on('submit', function() {
var bitch_errors = [];
var dog_errors = [];
// NOTE: Bitch and dog names need to be checked differently so we know which error is assigned to which input
$('.check_bitch_name').each( function(i, obj) {
// need to check each name for validity and duplication.
var entered_bitch_name = obj.value;
var pattern = /^[a-zA-Z,.]+\s[a-zA-Z,.]+(\s[a-zA-Z,.]+){0,}$/;
if(!pattern.test(entered_bitch_name)) {
bitch_errors[i+1] = "invalid";
} else {
// now to check for duplicates
$.post('/check-puppy-name', { name: entered_bitch_name }
).done(function (response) {
if(response == 'duplicate') {
bitch_errors[i+1] = "duplicate";
} else {
bitch_errors[i+1] = "";
}
});
}
});
$('.check_dog_name').each( function(i, obj) {
// need to check each name for validity and duplication.
var entered_dog_name = obj.value;
var pattern = /^[a-zA-Z,.]+\s[a-zA-Z,.]+(\s[a-zA-Z,.]+){0,}$/;
if(!pattern.test(entered_dog_name)) {
dog_errors[i+1] = "invalid";
} else {
// now to check for duplicates
$.post('/check-puppy-name', { name: entered_dog_name }
).done(function (response) {
if(response == 'duplicate') {
dog_errors[i+1] = "duplicate";
} else {
dog_errors[i+1] = "";
}
});
}
});
if(count(bitch_errors) == 0 && count(dog_errors) == 0) {
return true;
}
// loop through the errors and assign them to the correct input
$.each( bitch_errors, function( key, value ) {
if (value == "invalid") {
$('input[name="bitch_name['+key+']"]').parent().addClass('has-error');
$('input[name="bitch_name['+key+']"]').next('.error-message').html('Names must be at least two words, and only contain letters');
return false;
} else if(value == "duplicate") {
$('input[name="bitch_name['+key+']"]').parent().addClass('has-error');
$('input[name="bitch_name['+key+']"]').next('.error-message').html('Sorry, this name has already been taken');
return false;
}
});
$.each( dog_errors, function( key, value ) {
if(value != "") {
if (value == "invalid") {
$('input[name="dog_name['+key+']"]').parent().addClass('has-error');
$('input[name="dog_name['+key+']"]').next('.error-message').html('Names must be at least two words, and only contain letters');
return false;
} else if(value == "duplicate") {
$('input[name="dog_name['+key+']"]').parent().addClass('has-error');
$('input[name="dog_name['+key+']"]').next('.error-message').html('Sorry, this name has already been taken');
return false;
}
}
});
return false;
});
Basically, it first checks that the inputted name is valid, then posts off and checks for dupes. The issue is, even though it does the validity check (and prints errors accordingly) it seems to ignore the dupe check and carry on before it's even called back the first response.
How can I make sure it's finished it's checking before going on and adding the errors to the form? I've tried other solutions including attempting to implement the $.when functionality in jQuery but I don't really understand how to make it work. Any help appreciated.
First, write a function that returns an asynchronous promise to give you a value for one dog:
function checkDog(name) {
var pattern = /^[a-zA-Z,.]+\s[a-zA-Z,.]+(\s[a-zA-Z,.]+){0,}$/;
if(!pattern.test(name)) {
return $.Deferred().resolve("invalid");
} else {
return $.post('/check-puppy-name', { name: name } )
.then(function (response) {
if (response === 'duplicate') {
return 'duplicate';
} else {
return '';
}
});
}
}
Then you can write one that handles multiple dogs, also returning a promise (which won't itself be resolved until every dog has been checked):
function checkDogs(array) {
return $.when.apply($, array.map(checkDog));
}
Note that there's no DOM-related code yet. You can now write a function that gets the values from a bunch of DOM inputs and returns them in an array:
function getInputValues($selector) {
return $selector.get().map(function(el) {
return el.value;
});
}
So now (on submit) you can check your two sets of inputs and then finally when both of these are available, you can examine the results and update the DOM:
$('#puppy_form').on('submit', function() {
var bitch_names = getInputValues($('.check_bitch_name'));
var dog_names = getInputValues($('.check_dog_name'));
var bitch_promises = checkDogs(bitch_names);
var dog_promises = checkDogs(dog_names);
$.when(bitch_promises, dog_promises).then(function(bitch_errors, dog_errors) {
// update the DOM based on the passed arrays
...
});
});
You are right, ajax calls are like their name says asynchronous. Therefor you can only rely on the .done function. A simple solution would be to initialize a counter variable at the beginning for bitches and dogs and in the according done function you decrement it until it reaches zero. Then, also in the done function, you put an if that calls validation of the error arrays. Here is UNTESTED code to show what I mean:
$('#puppy_form').on('submit', function() {
/*
here you get the initial count for bitches and dogs
*/
var bitch_count = $('.check_bitch_name').length;
var dog_count = $('.check_dog_name').length;
var bitch_errors = [];
var dog_errors = [];
// NOTE: Bitch and dog names need to be checked differently so we know which error is assigned to which input
$('.check_bitch_name').each( function(i, obj) {
// need to check each name for validity and duplication.
var entered_bitch_name = obj.value;
var pattern = /^[a-zA-Z,.]+\s[a-zA-Z,.]+(\s[a-zA-Z,.]+){0,}$/;
if(!pattern.test(entered_bitch_name)) {
bitch_errors[i+1] = "invalid";
} else {
// now to check for duplicates
$.post('/check-puppy-name', { name: entered_bitch_name }
).done(function (response) {
if(response == 'duplicate') {
bitch_errors[i+1] = "duplicate";
} else {
bitch_errors[i+1] = "";
}
/*
now on every checked name you decrement the counter
and if both counters reach zero you can be sure you
checked all and only now you call your validation
*/
bitch_count--;
if(bitch_count === 0 && dog_count === 0) {
return validateErrors();
}
});
}
});
$('.check_dog_name').each( function(i, obj) {
// need to check each name for validity and duplication.
var entered_dog_name = obj.value;
var pattern = /^[a-zA-Z,.]+\s[a-zA-Z,.]+(\s[a-zA-Z,.]+){0,}$/;
if(!pattern.test(entered_dog_name)) {
dog_errors[i+1] = "invalid";
} else {
// now to check for duplicates
$.post('/check-puppy-name', { name: entered_dog_name }
).done(function (response) {
if(response == 'duplicate') {
dog_errors[i+1] = "duplicate";
} else {
dog_errors[i+1] = "";
}
/*
same here
*/
dog_count--;
if(bitch_count === 0 && dog_count === 0) {
return validateErrors();
}
});
}
});
}
/*
...and finally all code that should be processed after the ajax calls
*/
function validateErrors() {
if(count(bitch_errors) == 0 && count(dog_errors) == 0) {
return true;
}
// loop through the errors and assign them to the correct input
$.each( bitch_errors, function( key, value ) {
if (value == "invalid") {
$('input[name="bitch_name['+key+']"]').parent().addClass('has-error');
$('input[name="bitch_name['+key+']"]').next('.error-message').html('Names must be at least two words, and only contain letters');
return false;
} else if(value == "duplicate") {
$('input[name="bitch_name['+key+']"]').parent().addClass('has-error');
$('input[name="bitch_name['+key+']"]').next('.error-message').html('Sorry, this name has already been taken');
return false;
}
});
$.each( dog_errors, function( key, value ) {
if(value != "") {
if (value == "invalid") {
$('input[name="dog_name['+key+']"]').parent().addClass('has-error');
$('input[name="dog_name['+key+']"]').next('.error-message').html('Names must be at least two words, and only contain letters');
return false;
} else if(value == "duplicate") {
$('input[name="dog_name['+key+']"]').parent().addClass('has-error');
$('input[name="dog_name['+key+']"]').next('.error-message').html('Sorry, this name has already been taken');
return false;
}
}
});
return false;
});
You could use the async lib to manage these requests and collect the results which will then be passed into the final callback where you can process them.
I haven't tried to run this code but hopefully it will get you close enough if not already there.
async.parallel({
bitch_errors: function(callback) {
var bitch_errors = [];
async.forEachOf($('.check_bitch_name'), function(obj, i, cb) {
// need to check each name for validity and duplication.
var entered_bitch_name = obj.value;
var pattern = /^[a-zA-Z,.]+\s[a-zA-Z,.]+(\s[a-zA-Z,.]+){0,}$/;
if(!pattern.test(entered_bitch_name)) {
bitch_errors[i+1] = "invalid";
cb();
} else {
// now to check for duplicates
$.post('/check-puppy-name', { name: entered_bitch_name }
).done(function (response) {
if(response == 'duplicate') {
bitch_errors[i+1] = "duplicate";
} else {
bitch_errors[i+1] = "";
}
cb();
});
}
}, function () {
callback(null, bitch_errors);
});
},
dog_errors: function(callback) {
var dog_errors = [];
async.forEachOf($('.check_dog_name'), function(obj, i, cb) {
// need to check each name for validity and duplication.
var entered_dog_name = obj.value;
var pattern = /^[a-zA-Z,.]+\s[a-zA-Z,.]+(\s[a-zA-Z,.]+){0,}$/;
if(!pattern.test(entered_dog_name)) {
dog_errors[i+1] = "invalid";
cb();
} else {
// now to check for duplicates
$.post('/check-puppy-name', { name: entered_dog_name }
).done(function (response) {
if(response == 'duplicate') {
dog_errors[i+1] = "duplicate";
} else {
dog_errors[i+1] = "";
}
cb();
});
}
}, function () {
callback(null, dog_errors);
});
}
}, function(err, results) {
// you can now access your results like so
if(count(results.bitch_errors) == 0 && count(results.dog_errors) == 0) {
// ... rest of your code
});
Often in a response from a remote API call, I receive nested objects:
var response = {
data : {
users : [
{
name : 'Mr. White'
}
]
}
}
I want to check whether the first user's name is 'Mr. White', and would naturally want to write something like.
var existed = response.data.users[0].name === 'Mr. White'
However I cannot be sure if all the objects are present, so to avoid exceptions instead I end up writing:
var existed = response && response.data && response.data.users && response.data.users[0].name === 'Mr. White'
Is there a nicer way to do this? Another ugly option that comes to mind is:
var existed = false;
try {
var existed = response.data.users[0].name === 'Mr. White';
} catch(e) { }
In addition to vanilla javascript, I usually have underscore.js and jquery available too.
Edit:
Oops, noticed I asked a dupe of javascript test for existence of nested object key.
An interesting option based on those answers is:
var existed = (((response || {}).data || {}).users || [{}])[0].name === 'Mr. White';
You could hide this naughty try/catch block inside a function like this one :
function resolve(root, path){
try {
return (new Function(
'root', 'return root.' + path + ';'
))(root);
} catch (e) {}
}
var tree = { level1: [{ key: 'value' }] };
resolve(tree, 'level1[0].key'); // "value"
resolve(tree, 'level1[1].key'); // undefined
More on this : https://stackoverflow.com/a/18381564/1636522
I would use the try catch approach but wrap it in a function to hide the ugliness.
Instead of a try/catch, this should be done via checking whether each level in the object is defined or not.
go for
if(typeof(response)!="undefined"
&& typeof(response.data)!="undefined"
&& typeof(response.data.users)!="undefined"
&& typeof(response.data.users[0])!="undefined"
&& typeof(response.data.users[0].name)!="undefined"
) {
//executes only if response.data.users[0].name is existing
}
Here is a function which I used in one of my projects http://jsfiddle.net/JBBAJ/
var object = {
data: {
users: [
{
firstName: "White"
},
{
firstName: "Black"
}
]
}
}
var read = function(path, obj) {
var path = path.split(".");
var item = path.shift();
if(item.indexOf("]") == item.length-1) {
// array
item = item.split("[");
var arrayName = item.shift();
var arrayIndex = parseInt(item.shift().replace("]", ""));
var arr = obj[arrayName || ""];
if(arr && arr[arrayIndex]) {
return read(path.join("."), arr[arrayIndex]);
} else {
return null;
}
} else {
// object
if(obj[item]) {
if(path.length === 0) {
return obj[item];
} else {
return read(path.join("."), obj[item]);
}
} else {
return null;
}
}
}
console.log(read("data.users[0].firstName", object)); // White
console.log(read("data.users[1].firstName", object)); // Black
console.log(read("data.test.users[0]", object)); // null
The idea is to pass your path as a string along with your object. The idea was to prevent the throwing of an exception and receive just null as result of the path is wrong. The good thing is that the function works with every path and you don't need to write long if statements.