Why can't function access local variable in object declared outside - javascript

I was adding validation functions for my Node application, and want a custom message for the field I provide in that function. The issue is, some options need to have a default values, and others I can pass as parameters.
let field = 'email';
let options = {
'message': `${field} is required`
}
function validation(field, opt = options) {
console.log(opt.message);
}
validation('password');
validation('confirm', {message: 'Confirm password is required'})
But in this case, the output is
"email is required"
"Confirm password is required"
While I want the output to be
"password is required"
"Confirm password is required"
I also want to know how Javascript works for this code. How it is accessing all the stuff and how to get the required output.
Thanks

In your code you just create option object and you create it with message field = "email is required". And you never change it's value. As an alternative you may generate object each time you want it to be parametrized:
const field = 'email';
const getOptions = (field) => ({
'message': `${field} is required`
});
function validation(field, opt) {
opt = opt || getOptions(field);
console.log(opt.message);
}

let field = 'email';
let options = {
'message': `${field} is required`
}
This assigns options.message with the current value of field. Since you never changes its value, you get "email is required". The parameter named field in function validation(field, opt = options) is a different variable than the global one with the same name. And its value has no affect on the value of options.message because that assignment was already executed before the function was ever called.
Manipulating global objects is considered poor programming. Instead, you can create the object inside the function:
function validation(field) {
let opt = {
'message': `${field} is required`
}
console.log(opt.message);
}

message:`${field} is required`
Is the same as:
message: field + " is required"
which directly looks up field and results in:
message: "email is required"
To evaluate later you have to use a function that you pass the field name in which then returns the message:
const options = {
message: field => `${field} is required`,
}
function validation(field, opt = options) {
const msg = typeof opt.message === "function" ? opt.message(field) : opt.message;
console.log(msg);
}
validation('password');
validation('confirm', {message: 'Confirm password is required'})

Related

pull out data from array in localStorage

I have a function that takes data from an input and saves it as data in an array during registration. Now I want another function to check during login if the data exists and if it matches. How can I do this using javascript only?
In short, I need a function that checks if the data entered by the user exists and if so, logs him in.
function saveData() {
let name, email, password;
name = document.getElementById("username").value;
email = document.getElementById("email").value;
password = document.getElementById("password").value;
let user_records = new Array();
user_records = JSON.parse(localStorage.getItem("users"))
? JSON.parse(localStorage.getItem("users"))
: [];
if (
user_records.some((v) => {
return v.email == email;
})
) {
alert("Email wykorzystany");
} else {
user_records.push({
name: name,
email: email,
password: password,
});
localStorage.setItem("users", JSON.stringify(user_records));
}
}
I know that this is not how registration should be done. I am just doing it to learn new things.
this is a basic login, when you verify that the emmail and password are right, you can do wathever you want
function checkData() {
const name = document.getElementById('username').value;
const password = document.getElementById('password').value;
let user_records = JSON.parse(localStorage.getItem('users')) || [];
if (
user_records.find((user) => {
return user.name == name && user.password == password;
})
) {
alert('Logged in');
// do your things here
} else {
alert('wrong email or password');
// do your things here
}
}
<input id="username" />
<input id="password" />
<input type="submit" value="submit" onClick="checkData()" />
Extra:
This is a thing that you can do only for learning purpose, wich is to add a key to the local storage, for example: localStorage.setItem('loggedIn', true) and set in every page a check for this value, if is true show the page, if is false redirect to login. In the real world we use JWT tokens that contains all the information about the user etc... You can search for JWT token authentication on google and learn that, wich is really usefull in the front-end world
function saveAndTestUser() {
// here use const as they are not updated
const name = document.getElementById("username").value;
const email = document.getElementById("email").value;
const password = document.getElementById("password").value;
// Get all the records of the users
// prefer camelcase : not a rule though but it's better this way
const storedUsers = localStorage.getItem('users')
// if there are no stored users then assign empty array
// so that we don't get unexpected errors
const userRecords = storedUsers ? JSON.parse(storedUsers): []
// Checking if email already exists
if(userRecords.some(user => user.email === email)){
alert("user already exists")
return false; // stops the function execution
}
// If email doesn't exists then the code below will be executed
// Similar to if-else
// Add current record to existing records
const newRecords = [...storedUsers, {name, email, password}]
// Set the new record to storage
localStorage.setItem("users", JSON.stringify(newRecords));
}

validate multiple variables without writing multiple if else statements

Lets say we have 3 variables and I want to check them empty or not without using multiple if else blocks.
let firstName = "adem"
let lastName = "corona"
let email = "adamcorons#gmai.com"
If(firstName === " " && lastName !== " " && email !== " "){
Console.log("first name empty")
} else if .......
What is the best way of solving this?
Thanks a lot
You can avoid chained if-else stataments by returning directly from an if statement. For example, you can have a function such as this one:
function isInputValid({ firstName, lastName, email }) {
if (firstName === '') {
return false;
}
if (lastName === '') {
return false;
}
if (email === '') {
return false;
}
return true;
}
console.log(isInputValid({ firstName: 'adem', lastName: 'corona', email: 'adamcorons#gmai.com' }));
console.log(isInputValid({ firstName: 'adem', lastName: '', email: 'adamcorons#gmai.com' }));
Instead of a boolean value, you could also return an object containing an error message, so you can point out which field is missing.
You could try looping over a required array of fields like this to make it short and flexible:
const validate = (values, required) = {
let errors = {}
required.map(field => {
if (values[field] == '') {
errors[field] = 'Required';
}
}
return errors;
}
const required = ['firstName', 'lastName', 'email'];
const values = {
firstName = '',
lastname = 'test',
email = ''
}
const errors = validate(values, required);
console.log(errors);
// errors = { firstName: 'Required', email: 'Required' }
Making the values an object instead of individual parameters makes it possible to access them dynamically in a loop. This might work depending on what your needs and requirements are.
Then to check if errors exist, just see if the size of the object is 0 by converting it to an array:
if (Object.keys(errors).length == 0) {
// No errors, continue as valid
} else {
// There are errors, handle them as needed.
}
Another great way is to use YUP. It is possible to provide validation anywhere. I always use it. Here is an example.
const schema = Yup.object({
last_name: Yup.string()
.when('first_name', {
is: true,
then: Yup.string().required().label("Last Name"),
}),
});
You can use a similar as following. It also has many other amenities. You can find all the information from the official website below.
https://github.com/jquense/yup
I guess you can try something like this
// declare vars where you should, and add the vars you want to test in an object
let result = [];
let toTest= {
firstName = "adem",
lastName = "corona",
email: "adamcorons#gmai.com",
}
// declare this function to test your values
function testValue(){
Object.keys(toTest).map(key => {
if(!toTest[key]){
result.push(key);
}
}
}
// where you need, call your function to test your strings
testValue();
// in your result array you will have the keys of all the empty vars in your object
Note: if its for field validation you have some plugins like Yup(if Formik) or validate.js that are great for it ! have a look ! https://validatejs.org/
EDIT: Changed the response to an array so you can have all the results. I recommande you to set result as an object {key: errorMessage, ...} so its easier for you to use him after (ex: call the error.nameOfInput in your form to display the error.
EDIT2:
With object result would look like this
// declare vars where you should, and add the vars you want to test in an object
let error= {};
let toTest= {
firstName = "adem",
lastName = "corona",
email: "adamcorons#gmai.com",
}
// declare this function to test your values
function testValue(){
Object.keys(toTest).map(key => {
if(!toTest[key]){
error[key]={`${key} cannot be empty`};
}
}
}
// where you need, call your function to test your strings
testValue();
// in your render
<input name="firstName" ...props />
{error.firstName && <div className='error'>{error.firstName} </div> }
I believe you can't achieve this with plain if like in your example. You would need to have a validation schema and function that you run to validate, like most libraries do it. So you would need have some kind of initial object that couples the field names, their rules and error messages. But then you would need to use object with properties instead of plain variables. With those you cannot avoid checking them without separate if statements.
For reference check how joi or yup works. Although I do understand that using one of those might be too much for you, so I'd just check each field separately.

Regex validation not returning proper results upon function call

I have a component using a regex expression to check for email format validity. I have this attached as an onBlur function for the input that also runs on load as part of the React hook useEffect as soon as the page loads. The function is supposed to check for an empty field and fire an error message prompting the user to enter an email address or select opt out. If the email entered is the wrong format a separate error should fire. On load everything runs as expected with the initial error firing for an empty input. The issue occurs after a user enters the input field and leaves. The function fires and sets the error prompting a user to enter a valid email even when the input field is empty. I've swapped things out and initially tried to get around this by setting the first check as if(value === undefined || '' but so far I'm getting the same behavior. Any suggestions?
Functions for validation
function validateEmail(value) {
const errors = { hasError: false }
if (value === undefined || '') {
errors.email = 'Enter a valid email address or select Email Opt Out'
errors.hasError = true
return errors
}
if (!/\S+#\S+\.\S+/.test(value)) {
errors.email = 'Enter a valid email address'
errors.hasError = true
return errors
}
return errors
}
Can you just check to see if your string is empty before calling the validation method?
function onBlur(e) {
var stringValue = e.target.value || '';
if(stringValue !== '') checkIfCustomerEmailIsValid(e.target.value)
}
I was able to run a conditional regex expression to check if the value had either spaces or no value at all, and that did the trick! Code below for anyone's future reference
Solution
function validateEmail(value) {
const errors = { hasError: false }
if (/\s/g.test(value) || !value) {
errors.email = 'Enter a valid email address or select Email Opt Out'
errors.hasError = true
return errors
}
if (!/\S+#\S+\.\S+/.test(value)) {
errors.email = 'Enter a valid email address'
errors.hasError = true
return errors
}
return errors
}
Original
function validateEmail(value) {
const errors = { hasError: false }
if (value === undefined || '') {
errors.email = 'Enter a valid email address or select Email Opt Out'
errors.hasError = true
return errors
}
if (!/\S+#\S+\.\S+/.test(value)) {
errors.email = 'Enter a valid email address'
errors.hasError = true
return errors
}
return errors
}

Function returning function doesnt work for redux form field-level validation.

It`s a bit weird behavior, but I cannot actually figure out whats going on with my validation :)
I have a field level validation
export const confirm = (valueToConfirm, message) => (value, allValues) => {
if (value !== allValues[valueToConfirm]) {
return message;
}
return undefined;
};
And its using like
<Field
type="email"
name="confirmEmail"
component={TextInput}
validate={[required, email, confirm('email', 'Bla-bla-bla')]}
/>
And thats actually works only in case, when some another validation failed. So if user input correct all fields, and just confirmEmail would not match email - there would not be any validation error!
But if I change validation, so it would not be a function returning function - it works.
export const confirmEmail = (value, allValues) => {
if (!value || value !== allValues.email) {
return 'Bla-bla-bla';
}
return undefined;
};
P.S. Same for all Field-level validation, for example dynamic minLength validation.
I know you are using Redux Field however this is also easily achievable with Vanilla React.
<form>
<input
type="text"
name="username"
placeholder="name"
value={this.state.username}
onChange={this.handleChange}/>
</form>
handleChange(e) {
//check for validation here to have real time feedback to user when their
inputs match what you're looking for.
}
It looks like the confirm validation function is incorrect. The validation function you're using:
export const confirm = (valueToConfirm, message) => (value, allValues) => {
if (value !== allValues[valueToConfirm]) {
return message;
}
return undefined;
};
with the arguments you're using:
confirm('email', 'Bla-bla-bla')
means it will always validate. You're validating the value of the email input against the value of the email property in allValues, which is the value the email input. That is:
// value = test#example.com
// allValues = {email: 'test#example.com'}
// valueToConfirm = 'email'
// message = 'Bla bla bla'
if (value !== allValues[valueToConfirm]) {
return 'Bla bla bla';
}
which evaluates to:
if (test#example.com !== test#example.com) {
return 'Bla bla bla';
}
this conditional will always fail, meaning the validation will pass.

Stored value generating [object HTMLInputElement]

I have an indexedDB and using it for a login function. I'm trying to populate a form with the users information when they log in. However the form populates with [object HTMLInputElement] instead of the users info.
This is where I take the user (db key) to access the Object (the user)
EDITThis is my site where it's running: http://www3.carleton.ca/clubs/sissa/html5/admin.html
My site editor is updating it as I save, so there may be changes to the site script as I try new things.
This is where I take the user (db key) to access the Object (the user)
function loginCheck(user,pass){ db.transaction("users").objectStore("users").get(user).onsuccess = function(event) {
var loggedUser = event.target.result;
if(!loggedUser){
alert('Sorry, Username does not exist. Please try again.');
}else if(pass !== loggedUser.pw ){
alert('Incorrect log in combination. Please try again.');
}else{loggedIn(loggedUser);}
}
}
function loggedIn(loggedUser){
var u=loggedUser;
alert('Welcome '+u.fn+' '+u.ln+' to Macroplay');
//function to populate fields
alert('get values called');
getValues(u);
//session store
var signedin = 'user';
var username = u.userName;
newLocal(signedin,username);
alert('local storage set');
}
I use this function getValues to store the various fields I want from the object.
EDIT: I declared the variable test as global and stored the users first name (fn). The alerts show the correct name but the populate still gives me undefined.
var test;
function getValues(loggedUser){
var u = loggedUser;
alert('storing first name');
test = u.fn;
alert('First name = '+test);
lName = u.ln;
users = u.userName;
pass = u.pw;
email = u.em;
dob = u.dob;
tel = u.tel;
bio = u.bio;
school = u.scl;
alert('user values stored');
if(u.gender == 'M'){
gender[0].checked= true ;
}else{gender[1].checked= true ;}
}
This is the function I use to populate the form that's giving me [object HTMLInputElement]
function populateFields(){
alert('Name of populated field: '+test);
fName.value = test;
lName.value = lName;
users.value = users;
pass.value = pass;
email.value = email;
dob.value = dob;
tel.value = tel;
bio.value = bio;
terms.disabled = true;
school.value = school;
alert('populate fields done');
save.value = 'Update';
signin.innerHTML = 'Log Out';
registerLabel.innerHTML = 'Account Information';
//open user info form
var accountInfo = document.getElementsByTagName('details');
accountInfo[1].open = open;
}
Just look at one line:
fName.value = fName
You are setting the value property of fName to fName itself.
Rather than creating numerous global variables, just use loggedUser directly in populateFields():
fName.value = loggedUser.fn;
Edit: Looking at your site, I see the important bit you left out. populateFields() is being called after the page reloads. So, populateFields() does not have access to any variable created before the page reloaded.
Since I'm helping you with homework, I don't want to just hand you the answer on a silver platter. The trick is that the user data must be retrieved from the database and made available to populateFields() before it is called, or from within the function. You can make the user object available as a global variable, but it may be better to pass it in as a parameter.
You probably want to also cancel the form submission:
document.getElementById("loginForm").onsubmit = function (e) {
e.preventDefault();
}
And then just call populateFields() directly from loggedIn() instead of getValues().

Categories

Resources