I have a nested dictionary object with the following structure:
const [fieldErrorMessage, setFieldErrorMessage] = useState({
signinEmail: {
show: false,
message: "Error",
},
signinPassword: {
show: false,
message: "Error",
},
signupEmail: {
show: false,
message: "Error",
},
signupPassword: {
show: false,
message: "Error",
},
signupRegistrationToken: {
show: false,
message: "Error",
},
});
I created a function which should take in an array of nested dictionaries (e.g., signinEmail, signinPassword, ...) and set their value for their show key to false (without changing the value for the message property).
Here is my function:
function hideFieldErrors(setter, fieldNameArray) {
fieldNameArray.forEach((fieldName) => {
setter((prevState) => ({
...prevState,
fieldName: {
...prevState.fieldName,
show: false,
},
}));
});
}
When I call it, the show values do not change. Why is that?
I'm calling it via this code:
hideFieldErrors(setFieldErrorMessage, [
"signinEmail",
"signinPassword",
"signupEmail",
"signupPassword",
"signupRegistrationToken",
]);
The way you have used fieldName is not working as you expected. variable name(which is a string) should be wrapped with square brackets to make it an accessible property.
Try like below.
function hideFieldErrors(setter, fieldNameArray) {
fieldNameArray.forEach((fieldName) => {
setter((prevState) => ({
...prevState,
[fieldName]: {
...prevState[fieldName],
show: false,
},
}));
});
}
Related
I'd like to check at least one checked field is true and not disabled:
const test =
[ { Title:
{ Article: { checked: true, disabled: true }
, Desc: { checked: false, disabled: false }
} }
, { News:
{ Dashboard: { checked: false, disabled: false}
} }
]
I tried like this:
const checkedItems = () => {
let value = false;
test.forEach(el => Object.entries(el).forEach(([title, checkboxProps]) => {
Object.entries(checkboxProps).forEach(([name, config]) => {
if (config["checked"] && !config["disabled"]) {
value = true
}
})
}))
return value;
};
A couple of flatMaps with Object.values can do it pretty cleanly.
const test = [{
Title: {
Article: {
checked: true,
disabled: true
},
Desc: {
checked: false,
disabled: false
}
}
}, {
News: {
Dashboard: {
checked: false,
disabled: false
}
}
}];
const atLeastOneCheckedAndNotDisabled = test
.flatMap(Object.values)
.flatMap(Object.values) // now we have an array of [{checked, disabled}]
.some(innerObj => innerObj.checked && !innerObj.disabled);
console.log(atLeastOneCheckedAndNotDisabled);
You don't care about the keys, only the values, so Object.values will make things easier to work with than Object.entries.
Here is a data structure I'm working with
masterObject: {
Steps: [
{
step: {
required: false,
},
step: {
required: false,
},
step: {
required: false,
},
},
]
}
I'd like to loop through Steps array and check if all each step object contains propery required that is equal to false.
If all required steps are false I'd like to attach a property to the masterObject object of
masterObject.isObjectWithNoRequiredSteps = true;
which would look like this after the code runs
masterObject: {
Steps: [
{
step: {
required: false,
},
step: {
required: false,
},
step: {
required: false,
},
},
]
isObjectWithNoRequiredSteps: true
}
If theres an example where one of the step object required is true I'd like to set the new property to false
masterObject.isObjectWithNoRequiredSteps = false;
masterObject: {
Steps: [
{
step: {
required: true,
},
step: {
required: false,
},
step: {
required: false,
},
},
]
isObjectWithNoRequiredSteps: false
}
What is the best approach to setting this property on the higher level object?
The object inside Steps array has multiple attributes/keys with the same name step. This is not allowed in JavaScript. Consider changing your data structure as follows
masterObject: {Steps: [{
step1: { required: false, },
step2: { required: false, },
step3: { required: false, },
}, ] }
Now, to determine if each step inside Steps array is false, use following code
masterObject.isObjectWithNoRequiredSteps = Object.keys(masterObject.Steps[0]).reduce(function(result, nextStep){
const isRequired = masterObject.Steps[0][nextStep].required;
return !isRequired && result;
}, true);
Maybe complex question but i want try...
In vue component, (which is modal window for administration slider with few slide types like video, image...) i want have whole modal structure in json object. So i was created baseTypeStructure (inputs common for all slide types), avalibleTypeStructure (inputs for specific slide type) and activeTypeStructure (complete inputs structure after pick some slide type)
But i am in vue (and js) pretty new and i have complications with reactivity...
So if you pick some slide type i bind activeTypeStructure from baseTypeStructure and specific type from avalibleTypeStructure. (by type key)
But i have problems with reactivity... On one side i need reactive behavior because there are input values... on other side there are informations about visibility... and if i change in activeTypeStructure visibility to true, that value is changed in avalibleTypeStructure if i make clone (without reactivity), every change slide type i do reset all values...
Can anybody help with this? Resolve it this way (as one json format for whole modal). Maybe absolute bad thinking?
data() {
return {
detailModalIsOpened: false,
loading: false,
action: '',
title: 'default',
baseTypeStructure: {
inputs: {
name: {
visible: true,
value: ''
},
type: {
visible: true,
value: '',
selected: [],
slideTypes: []
},
duration: {
visible: true,
value: 5000
},
image: {
inputs: {
imageFile: {
visible: false,
value: ''
}
}
},
video: {
inputs: {
videoFile: {
visible: false,
value: ''
}
}
}
}
},
avalibleTypeStructures: {
none: {},
image: {
// name: 'image',
inputs: {
imageFile: {
visible: true,
value: ''
}
}
},
video: {
// name: 'video',
inputs: {
videoFile: {
visible: true,
value: ''
}
}
}
},
activeTypeStructure: {},
}
}
I have this state in react as following
state = {
profile: {
name: { value: '', isRequired: true, hasError: false, order:1 },
industry: { value: '', isRequired: false, hasError: false, order:2 },
address: { value: '', isRequired: false, hasError: false. ordere:3},
crn: { value: '', isRequired: true, hasError: false, order:4 },
website: { value: '', isRequired: false, hasError: false, order:5 },
employeesNbr: { value: '', isRequired: true, hasError: false, order:6 },
phoneNumber: { value: '', isRequired: true, hasError: false, order:7 },
userRole: { value: '', isRequired: true, hasError: false, order:8},
personCheck: { value: false, isRequired: true, hasError: false, order:9 },
},
};
And I have the following logic that was using the profile values as keys
handleInputChange = (key: string, value: string) => {
const { profile } = this.state;
profile[key].value = value;
profile[key].hasError = false;
this.setState({ profile });
};
handleProfileFormSubmit = () => {
const { profile } = this.state;
let errorExists = false;
const arr = [];
Object.keys(profile).forEach((key: string) => {
const element = profile[key];
if (
(typeof element.value !== 'boolean' &&
element.isRequired &&
isEmpty(element.value)) ||
(element.isRequired && !element.value)
) {
element.hasError = true;
errorExists = true;
arr.push(key);
}
});
How can I change the handleProfileFormSubmit logic to go through the foreach loop using the order as keys so it goes through them from lower to higher number. This is important because changing the input fields in the front end without changing the order in the state part makes undefined errors when passing values?
For that you will have to do a sort first. Instead of Object.keys(profile).forEach do this.
let sortedKeys = Object.keys(profile).sort((a, b)=> profile[a].order - profile[b].order)
sortedKeys.forEach((key: string) => {
const element = profile[key];
})
I am using a pair of custom keywords to ease our validation needs. So far it has worked well everywhere... but now I'm not getting the errors to persist and be detected after validation.
ajv.addKeyword('batch', {
compile: function validator(batch) {
const { limit, items } = batch;
const arraySchema = {
type: 'array',
items,
minItems: 1,
maxItems: limit,
};
return ajv.compile({
oneOf: [
arraySchema,
items,
],
});
},
errors: false,
});
ajv.addKeyword('customValidator', {
type: 'string',
validate: function validate(schema, data) {
try {
if (data.length > 500) {
throw new Error('should be <= 500 characters');
}
const { type } = myCustomValidator.parse(data);
if (schema === true || schema.includes(type)) {
return true;
}
throw new TypeError(`${data} must be one of the following: ${schema}`);
} catch (error) {
if (!validate.errors) {
validate.errors = [];
}
validate.errors.push(error);
return false;
}
},
errors: true,
});
With then a schema like this:
{
type: 'object',
required: [
'requiredFieldName',
],
properties: {
requiredFieldName: {
batch: {
items: { customValidator: ['allowedType'] },
limit: 100,
},
},
optionalFields: { customValidator: ['allowedType1', 'allowedType2'] },
},
}
I then created a test that would fail causing myCustomValidator.parse to throw.
{
requiredFieldName: ['blah', 'blah'],
}
Using console.log I can see that it throwing and is being caught and being added to the validator.errors. I expect the validation to fail, however at the end of the day it says it passes. Any ideas of what I am doing wrong?
Note: If I change the definition of the items in batch of the schema to type: 'integer' it fails as expected.
Turns out it works if you specify type: 'string' in addition to the customValidator in the schema:
{
type: 'object',
required: [
'requiredFieldName',
],
properties: {
requiredFieldName: {
batch: {
items: { type: 'string', customValidator: ['allowedType'] },
limit: 100,
},
},
optionalFields: { customValidator: ['allowedType1', 'allowedType2'] },
},
}
Don't know why, but this is now working.