Display dynamically nested object as a table in Javascript - javascript

I'm trying to use nested object to display fields in a table. (using Material UI and formik)
My rows table are ordered by year so I have:
<Formik initialValues={{
2018: {
other: 1,
year: 2018,
number: 1,
name: "Jim",
surname: "Jon",
},
2019: {
other: 1,
year:2019,
number: 1,
name: "Tun",
surname: "Ton",
}}
onSubmit={()=>{}}>
To use nested Object I could do:
<Form>
<Field name="2018.name" />
<Field name="2019.name" />
<button type="submit">Submit</button>
</Form>
But if my response return something different from mine It don't works.
How can I fix it?
I'd tried like this:
export const REPORT_IVASS_FORM_INIT_VALUES = Object.freeze({
"year": '',
"name": '',
"surname": '',
"number": '',
"other": '',
})
and after:
response.map(el =>
<Field
name={response.2018[el.key]
type="text"
}
/>
)
but firstly response.2018[el.key], after response.2019[el.key] and so on.
But obviously it don't work.

You don't have to use key inside it , you can use it like this
{
response.map((el,index) => {
return <div key={index}>
<Field
name={response.name[el.index]
} type="text" />
</div>
})
}

looking at your initial value, it looks as if you receive an object, ordered by year where the year is unique. You should use a map function to return JSX for each year, and use a unique key for the key prop.
You can use Object.values to iterate and map over your initial data or api data, if its the same (an object) as your initial data
<Form>
{Object.values(initialValues).map((value) =>
<Field
key={value.year}
name={value.name}
type="text"
/>
)}
<button type="submit">Submit</button>
</Form>

Related

zod validation for an array of objects

I am trying to add validation for a form that has a checkbox selection with a number input next to each checkbox. A user selects a profession checkbox and then enters the number of years of experience they have in the input next to it. The array looks like this (experience has a default of 1):
const fieldsOfEng = [
{
id: "ELECTRICAL",
name: "Electrical",
experience: 1,
},
{
id: "MECHANICAL",
name: "Mechanical",
experience: 1,
}
]
This is how the schema would look if I was just verifying that the user selected one of the options in my professions array
export const userInfoSchema = z.object({
professions: z
.string()
.array()
.refine((val) => val.some(profession =>
fieldsOfEng
.map((field) => field.name)
.includes(profession)))
})
with the input being registered via react-hook-form like:
{fieldsOfEng.map((field) => {
return (
<input
{...register("professions")}
value={field.name}
type="checkbox"
/>
)}
--------------------WHAT I WANT:
I'd like to add an 'experience' field to my schema so I it would look something like (but this isn't correct):
professions: z
.array(
z.object({
name: z.string(),
experience: z.number(),
})
)
.refine(({name}) =>
name.some(({ profession }) =>
fieldsOfEng.map((field) => field.name).includes(profession)
)
)
.refine(({ experience }) => {
experience.some((exp) => exp > 1);
}),
And I think my form would look something like:
{fieldsOfEng.map((field) => {
return (
<input
{...register("professions.name")}
value={field.name}
type="checkbox"
/>
<input
{...register("professions.experience")}
value={field.experience}
type="number"
/>
)}
I can always experiment with the form but my main concern is the schema.
This answer is an update on my progress so far. I closer to getting my schema validation working correctly but for some reason it is not submitting. I could solve this faster if I could get react-hook form's errors to output correctly but since I'm using template literals when I'm registering the inputs, the error is just outputting nothing. I DO know there is an error though because my truthy error outputs the "professions:" string that I added in the p-tag.
Data for checkboxes in form (this is the data I'm validating via zod):
const fieldsOfEng = [
{
id: "ELECTRICAL",
name: "Electrical",
experience: undefined,
},
{
id: "MECHANICAL",
name: "Mechanical",
experience: undefined,
},
Schema for validation: I'm checking that the name they select is included in my fieldsOfEng array and making sure the experience is greater than 1.
const userInfoSchema = object({
professions: z
.object({
name: z
.string()
.refine((name) =>
fieldsOfEng.map((field) => field.name).includes(name)
),
experience: z.number().refine((experience) => experience > 1),
})
.array(),
});
React hook form:
type userInfoType = z.infer<typeof userInfoSchema>;
const {
register,
watch,
handleSubmit,
formState: { errors, isSubmitting },
} = useForm<userInfoType>({
resolver: zodResolver(userInfoSchema),
});
Form: Notice I am using template literals for registering each input. It took me forever to figure this out. So far this is the only way I've been able to get the data to output correctly (via react-hook-form's 'watch' method).
<fieldset>
<legend>Practicing fields of engineering</legend>
{fieldsOfEng.map((field, i) => {
return (
<div key={field.id}>
<div>
<input
{...register(`professions.${i}.name`)} <--REGISTERED HERE
value={field.name}
type="checkbox"
/>
<div>
<input
{...register(`professions.${i}.experience`)} <--REGISTERED HERE
type="number"
value={field.experience}
placeholder="Yrs Experience"
/>
</div>
</div>
<div>
{errors.professions && (
<p>
professions:
{errors.professions.message}
</p>
)}
</div>
</div>
);
})}
</fieldset>

Update specific object property value in an array of objects while using inline function

My goal is to update the price field for an individual object.
Here is my selectedCurrenciesArray:
const [selectedSwapCurrencies, setSelectedSwapCurrencies] = useState([
{
symbol: null,
logo: null,
price: 0
},
{
symbol: null,
logo: null,
price: 0
},
]);
I'm mapping through an Input component like so...
{Object.values(selectedSwapCurrencies).map((currency, index) => {
return (
<Input
onChange={(e) => setSelectedSwapCurrencies({
...selectedSwapCurrencies,
[selectedSwapCurrencies[index].price]: e.target.value,
})
/>
)
})}
However, each time I update the input, it deletes the first object in the array entirely.
Try this (not tested)
selectedSwapCurrencies.map((currncy,index)=>{
return (
<Input
onChange={(e) => setSelectedSwapCurrencies({
...selectedSwapCurrencies,
[selectedSwapCurrencies[index].price]: e.target.value,
})
/>
)
})

React Forms : How to make input data as list objects with same input name?

I'm Posting input data to API's where the API's structure as array list with n objects.
Problem: The problem I'm facing is while handling the change, i'm replacing the data instead of inserting as an another object.
My code as follows:
the objects in the list is dynamic not limited to two
in Constructor->
this.state={
formData: [
{
information: "",
from: "",
to: "",
},
{
information: "",
from: "",
to: "",
},
],
}
handling the Change as follow:
handleChange = (e) => {
const { name, value } = e.target;
const { formData} = this.state;
this.setState({
formData: {
...formData,
[name]: value,
},
});
Handle submit:
handleSubmit = (e) => {
e.preventDefault();
console.log(this.state.formData);
}
Form fields as follows:
<form onSubmit={this.handleSubmit}>
<div>
<input type="text" name="information" onChange={this.handleChange} />
<input type="text" name="from" onChange={this.handleChange} />
<input type="text" name="to" onChange={this.handleChange} />
</div>
<div>
<input type="text" name="information" onChange={this.handleChange} />
<input type="text" name="from" onChange={this.handleChange} />
<input type="text" name="to" onChange={this.handleChange} />
</div>
.
.
.
<button> + Add new Set (div) </button>
<button type="submit"> Submit </button>
</form>
I'm aware that mistake is handing logic but i tried lot to correct. Please help me out.
Edit: Expected output:
[
{
"information": "info 1",
"from": 1,
"to": 50
},
{
"information": "info 2",
"from": 51,
"to": 80
},
{
"information": "info 3",
"from": 81,
"to": 100
}
]
If you want to insert a third object to formData you need to add curly brackets {} to frame [name]:value as an object. Also formData is an Array so it must be with square brackets not curly ones.
this.setState({
formData: [
...formData,
{ [name]: value }
],
The best approach is to use prevState in setState.
this.setState(prevState => ({...prevState, [name]: value }))
This will guarantee that you always use the previous state. setState is an async function, if you use formData it will not guarantee that the changes that you already dispatch are in place to be used.
state={
formData: [
{
information: "",
from: "",
to: "",
},
{
information: "",
from: "",
to: "",
},
],
information:"",
from:"",
to:""
}
...
<input type="text" name="information" onChange={(event)=>{this.setState({information:event.target.value})} />
<input type="text" name="from" onChange={(event)=>{this.setState({from:event.target.value})} />
<input type="text" name="to" onChange={(event)=>{this.setState({to:event.target.value})} />
...
handleSubmit = (e) => {
e.preventDefault()
const data={ information: this.state.information,
from: this.state.form,
to: this.state.to}
this.setState({formData:[this.state.formData,data]})
console.log(this.state.formData);
}

Formik handle an array of objects

i'm trying to implement array of objects of a structure like this
selectedItems: [
{
_id: ""
}
]
what I want to do is when the user selects for example 2 or more _id, I want the structure to be like this
[
{
_id: "123"
},
{
_id: "456"
},
{
_id: "789"
},
]
but what I currently get with my implementation is an array of _id that will contain several items like this
[
{
_id: ["123", "456", "789"]
}
]
I followed the documentation of formik which suggests to implement this solution when we have an array of objects.
my implementation
const GetSelectedItems = () => {
return (
<Formik
initialValues={{
selectedItems: [{
_id: ""
}]
}}
onSubmit={values => {
console.log(values)
}}
render={({values, handleChange}) => (
<Form>
<FieldArray
name="selectedItems"
render={arrayHelpers => (
<div>
{values.selectedItems && values.selectedItems.length > 0 ? (
values.selectedItems.map((item, index) => (
<div key={index}>
<Field as="div"
name={`selectedItems${[0]}._id`}
>
<select name={`selectedItems.${[0]}._id`}
multiple={true}
className="form-control"
value={item._id}
onChange={event => {
handleChange(event);
}}
>
<option value={values.selectedItems._id}>
Choisir items
</option>
{itemList(items)} // data from api
</select>
</Field>
</div>
))
) : null}
<div>
<div>
<button type="submit">Submit</button>
</div>
</div>
</div>
)}
/>
</Form>
)}
/>)
}
You don't need to give a name prop to select's option components, just remove it and your code will run as expected:
// Removed name props from this component
<option key={option._id} value={`${option._id}`}>
{option._id}
</option>

ReactJS | Failed Prop-Type Expected String got Object instead

I am using React-Select, in my form with Formik. I have the following array of Objects, that comes from the API, and which I map through it, in order to transform the data in the format React-select wants.
Here is the mapped through array of data, that I supply to the select.
[ { "label": "picard", "value": "picard" }, { "label": "riker", "value": "riker" } ]
It has a label and value pair. Exactly like react-select want it to. But in my Formik HOC, I have the PropType for the values of the data property(e.g: groups), to be a string, as it should, since the server will give me and receive unique strings.
Here is the PropType:
values: PropTypes.shape({
useraname: PropTypes.string,
email: PropTypes.string,
password: PropTypes.string,
confirmPassword: PropTypes.string,
group: PropTypes.string
}),
For that reason, every time i try to select an item from the dropdown, I get this:
checkPropTypes.js:19 Warning: Failed prop type: Invalid prop `values.group` of type `object` supplied to `AddEditUser`, expected `string`.
Here is the select element, and the mapping function:
mapListGroupToSelect = () => {
const { groups } = this.state;
return groups.map(group => ({
label: group.name,
value: group.name
}));
};
const groups = this.mapListGroupToSelect();
return (
<div className="pt-2">
<label className="font-weight-bold">
Select Group <Asterisk />
</label>
<Select
placeholder="Select a Group (Just One)"
onChangeCallback={handleChange}
type="simpleSelect"
options={groups}
isMulti={false}
id="group"
onChange={options => setFieldValue('group', options)}
/>
{JSON.stringify(groups, null, 2)}
<ErrorMessage name="group" component="div" className="text-danger" />
</div>
)
Do you have any ideas on how to solve this?

Categories