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>
Related
i am tying to build a multi select of items. my backend data structure is an array of objects like this
{
"selectedItems": [
{"_id" : ""}
]
}
the problem with react-select is when i select one or many items, the structure does not match with my backend route, it displays like this
{
"selectedItems": [
{"value" : "", label : ""}
]
}
i am working with Formik to manage the form and you can also see the result on this sandbox on console log
const ItemSelected = () => {
const items = [
{
_id : "123", name : "john", desc : 'eb'
},
{
_id : "456", name : "doe", desc : 'ec'
},
{
_id : "789", name : "seal", desc : 'ef'
}
]
const itemList = (options) => {
return (
options &&
options.map(option => {
return {
value: option._id,
label: option.name
};
})
);
}
return(
<div>
<Formik
initialValues={{
selectedItems : []
}}
onSubmit={values => {
console.log(values)
}}
>
{({
values,
handleSubmit,
setFieldValue
}) => (
<Form onSubmit={handleSubmit}>
<div className="row">
<div className="col">
<Select
isMulti
name={`selectedItems`}
value={values.selectedItems}
onChange={e=>setFieldValue(`selectedItems`, e)}
options={itemList(items)}
className="basic-multi-select"
classNamePrefix="select"
/>
</div>
<div className="col">
<button type="submit">
submit
</button>
</div>
</div>
</Form>
)}
</Formik>
</div>
)
}
You need to use map to create the data structure you want when you handle submit.
onSubmit={values => {
if(values.selectedItems){
const data = values.selectedItems.map(value => ({_id: value.value}))
console.log(data);
}
}}
{
title: 'Stores', field: 'Depolar',
editComponent: props => (
<Select
name="Depolar"
type="text"
value={that.state.selectedStores}
multiple
input={<Input />}
onChange={e => {
that.setState({
selectedStores:e.target.value
})
let store=that.state.stores.find(item => !that.state.selectedStores.includes(item.Adi)
)
Object.keys(store.Urunler).map(function (key,index) {
if(store.Urunler[key]){
that.setState({
products:that.state.products.push(store.Urunler[key])
});
}
})
console.log(that.state.products)
}}
>
{that.state.stores.map(item=> {
return <MenuItem value={item.Adi}>{item.Adi}</MenuItem>
})}
</Select>
)
},
{
title: 'Products', field: 'Urunler',
editComponent: props => (
<Select
name="Urunler"
type="text"
value={that.state.selectedProducts}
input={<Input />}
multiple
onChange={e => {
that.setState({selectedProducts:e.target.value})
}}
>
{/* Aşağıdaki mapde hata veriyor. Objecte çevir*/}
{that.state.products ? Object.keys(that.state.products).map(function (key,index) {
return <div><MenuItem value={that.state.products[key].Adi}>{that.state.products[key].Adi} </MenuItem></div>
}
):null}
</Select>
)},
getting error. I try to do multiple select and change second select's data with first Select's Onchange on material-table row.
I am using firebase.
I am taking this error when "store.Urunler" is null or undefined. and also my products doesnt list on MenuItem mean Select is Empty but state.products has data
How can i get over this error. and also i wanna add to Urunler Select an input. I know i should call function for onChanges but if it works i will do later
{
title: 'Stores', field: 'Depolar',
editComponent: props => (
<Select
name="Depolar"
type="text"
value={that.state.selectedStores}
multiple
input={<Input />}
onChange={async e => {
let odata=[]
this.setState({
selectedStores:e.target.value,
products:odata
})
let store= await that.state.stores.filter(item => (
!that.state.selectedStores.includes(item.Adi))
);
console.log(store)
this.changeProducts(store);}}
>
{that.state.stores.map(item=> {
return <MenuItem value={item.Adi}>{item.Adi}</MenuItem>
})}
</Select>
)
},
{
title: 'Products', field: 'Urunler',
editComponent: props => (
<Select
name="Urunler"
type="text"
value={that.state.selectedProducts}
input={<Input />}
multiple
onChange={e => {
that.setState({selectedProducts:e.target.value})
}}
>
{/* Aşağıdaki mapde hata veriyor. Objecte çevir*/}
{that.state.products ? Object.keys(that.state.products).map(function (key,index) {
return <div><MenuItem value={that.state.products[key].Adi}>{that.state.products[key].Adi} </MenuItem></div>
}
):null}
</Select>
)
},
changeProducts( store){
var that=this;
Object.keys(store).map(function (key,index) {
let data=store[key].Urunler;
if(data){
that.setState({
products:{...that.state.products,...data}
});
}
})
}
I want my form to submit price data as an array format, currently, my form has a map, look like
{data.Type &&
<div>
{data.Type.map((datamapped)=>
<div key={datamapped._id}>
<p>{datamapped.TypeName}</p>
<Form.Item>
{getFieldDecorator(`price.${datamapped._id}.basePrice`)(
<Input placeholder="Base Price"/>,
)}
{getFieldDecorator(`price.${datamapped._id}.higherPrice`)(
<Input placeholder="Higher Price"/>,
)}
</div>
)}
</div>
}
Here I mapping my Type here and included, basePrice and higherPrice fields
result is :
price:
{
'5dc2913cf9e2372b11db4252': { basePrice: '0', higherPrice: '0' },
'5dc2a109f9e2372b11db4253': { basePrice: '0', higherPrice: '0' }
},
I want the above result as an array format how to do it?
Another way to get values as an array is to provide an array as FormItem name attribute like this:
<FormItem name={['wrapperName', index, 'propertyName']}>
<Input/>,
</FormItem>
Try change datamapped._id to [index]
{data.Type &&
<div>
{data.Type.map((datamapped, index)=>
<div key={datamapped._id}>
<p>{datamapped.TypeName}</p>
<Form.Item>
{getFieldDecorator(`price[${index}].basePrice`)(
<Input placeholder="Base Price"/>,
)}
{getFieldDecorator(`price[${index}].higherPrice`)(
<Input placeholder="Higher Price"/>,
)}
</div>
)}
</div>
}
You can try this:
var json = {
price: {
"5dc2913cf9e2372b11db4252": { basePrice: "0", higherPrice: "0" },
"5dc2a109f9e2372b11db4253": { basePrice: "0", higherPrice: "0" }
}
};
json.price = Object.entries(json.price);
console.log(json);
📒my Dynamic Form's note
antd#3.x
const MyForm = ({ form = {} }) => {
const { getFieldValue, getFieldDecorator } = form;
const [defaultData, setDefaultData] = useState({});
const add = ()=>{
// 🚩
defaultData.price.push({basePrice: 'zz',
higherPrice: 'hzz',key: new Date().getTime()})
}
return (
<Form>
<Form.Item>
{Array.isArray(defaultData.price) && // 🚩
defaultData.price.map((datamapped, index) => {
return (
<div key={datamapped.id||datamapped.key}>
{getFieldDecorator(`price[${index}].basePrice`, { // 🚩
initialValue: datamapped.basePrice,
})(<Input placeholder="Base Price" />)}
{getFieldDecorator(`price[${index}].higherPrice`, {
initialValue: datamapped.higherPrice,
})(<Input placeholder="Higher Price" />)}
</div>
);
})}
</Form.Item>
<Button onClick={add}> + </Button>
</Form>
);
};
export default Form.create()(MyForm);
data structure
|-- price array[object]
|-- basePrice string
|-- higherPrice string
🚩must use useState to assist setFieldsValue
const [defaultData, setDefaultData] = useState({})
setDefaultData({
price: [
{
basePrice: 'xx',
higherPrice: 'hxx',
},
{
basePrice: 'yy',
higherPrice: 'hyy',
},
],
});
⚠️ setFieldsValue only works in the first calling
Good evening readers!
I'm working to a simple shopping cart single page application using react and redux!
That's the situation:
listOfCategories: ["Basic", "Hardware"]
listOfItems : [
{
fields: {
category: "Basic",
name: "Starter",
...
},
...
},
{
fields: {
category: "Basic",
name: "Entertainment",
...
},
...
},
{
fields: {
category: "Hardware",
name: "STB",
...
},
...
}
]
In my component, inside the render method, there is:
render() {
return (
<div>
<div>
Catalog
{this.props.listOfItems.map(item => (
<Product
id={item.fields.productexternalid}
name={item.fields.productname}
category={item.fields.SKYDE_Product_Category__c}
clicked={() => this.addToCart(item)}
costOneTime={item.fields.baseonetimefee}
costRecurring={item.fields.baserecurringcharge}
eligible={item.fields.eligible}
visible={item.fields.visible}
></Product>
))}
</div>
</div>
);
}
The result is something like this:
I just want to render an accordion filled with the category name, items grouped by category under the accordion:
Basic --> item.category
Starte --> item.name
Entertainment --> item.name
Hardware --> item.category
STB --> item.name
.map() and .filter() function will be useful, but i don't really know how to manage this case.
Any help will be appreciated!
map() and filter() are definitely useful in this case.
render() {
// in case "listOfCategories" is not predefined
let listOfCategories = listOfItems.map(item => item.fields.category)
// sort and remove duplicates
listOfCategories = listOfCategories.sort().filter((v, i) => listOfCategories.indexOf(v) === i);
return (
<div>
{listOfCategories.map(cat => (
// You probably had this `Category` component around
<Category key={cat} name={cat} {...catProps}>
{listOfItems.filter(item => item.fields.category === cat).map(item => (
<Product
key={item.fields.id}
id={item.fields.id}
name={item.fields.name}
{...itemProps}
/>
))}
</Category>
))}
</div>
);
}
Basic
<div>
Basic
{this.props.listOfItems.filter(item => item.fields.category ==="Basic").map(item => (
<Product
id={item.fields.productexternalid}
name={item.fields.productname}
category={item.fields.SKYDE_Product_Category__c}
clicked={() => this.addToCart(item)}
costOneTime={item.fields.baseonetimefee}
costRecurring={item.fields.baserecurringcharge}
eligible={item.fields.eligible}
visible={item.fields.visible}
></Product>
))}
</div>
Hardware
<div>
Basic
{this.props.listOfItems.filter(item => item.fields.category ==="Hardware").map(item => (
<Product
id={item.fields.productexternalid}
name={item.fields.productname}
category={item.fields.SKYDE_Product_Category__c}
clicked={() => this.addToCart(item)}
costOneTime={item.fields.baseonetimefee}
costRecurring={item.fields.baserecurringcharge}
eligible={item.fields.eligible}
visible={item.fields.visible}
></Product>
))}
</div>
I need to pass multiple value by using onChange when I select the option, but I can not select single option . it select whole objects .
Here is my code .
const test = [{id:1, name:'test, value:{x:10}}]
<Select
showSearch
style={{ width: 200 }}
placeholder="Select a person"
optionFilterProp="children" >
{test.map(item =>(
<Option value={item.id, item.value}>{item.name}</Option>
))}
</Select>
is there an alternative solution to fix this problem
Use Custom select instead of select.
add multiple values in "eventKey" and on select handler you will find that value array.
import { ButtonToolbar,DropdownButton,MenuItem } from "react-bootstrap";
<ButtonToolbar className="snap-fitness-wrapper">
<DropdownButton
title={this.state.selectedFacilityName}
onSelect={this.onSelectFacility}
id="snap" >
{
this.state.facilities.map(facility => (
<MenuItem
key={facility.facilityId}
eventKey={[facility.facilityId, facility.name]}
value={facility.facilityId}
> {facility.name} - {facility.facilityId}{" "}
</MenuItem>
))
}
</DropdownButton>
</ButtonToolbar>
You can try below code.
const test = [{id:1, name:'test, value:{x:10}}]
<Select
multiple
showSearch
style={{ width: 200 }}
placeholder="Select a person"
optionFilterProp="children" >
{test.map(item =>(
<Option value={item.id, item.value}>{item.name}</Option>
))}
</Select>
If you are using react-select you can add isMulti common props to enable multiple selection for your option , please refer: https://react-select.com/props
BTW, react-select default use value label match, you have to remap for your case
const options = [
{ id: 1, name: 'test1', value:{x:10} },
{ id: 2, name: 'test2', value:{x:20} },
]
<Select
... // other code
isMulti
getOptionValue={option => option.value.x}
/>
It support multiple select , sample link : sandbox
I suppose you are using antd for select and want to get the object properties inside handleChange of select.
A simple method would be to send the id or other parameter and get the object through that.
import React from "react";
import ReactDOM from "react-dom";
import { Select } from "antd";
import "./styles.css";
const { Option } = Select;
const test = [
{ id: 1, name: "test1", value: { x: 10 } },
{ id: 2, name: "test2", value: { x: 11 } },
{ id: 3, name: "test3", value: { x: 12 } }
];
class App extends React.Component {
handleChange = id => {
const clickedOption = test.find(item => item.id === id);
const value = `${clickedOption.id}, ${clickedOption.value.x}`;
console.log(value);
};
render() {
return (
<div className="App">
<Select
onChange={this.handleChange}
style={{ width: 200 }}
placeholder="Select a person"
optionFilterProp="children"
>
{test.map((item, index) => (
<Option value={item.id} key={index}>
{item.name}
</Option>
))}
</Select>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Hope that helps!!!
The simple way of doing this can be like this:
let options = test.map(item => {
return <option value={item.value} onClick={(item) => this.yourhandlerfunction(item)}>item.name</option>
})
render(){
return(
<Select>
{options}
</Select>
)
}
Hope this helps!
You can just pass a second variable in onChange=((e) => this.handleChange(e, item.value) property
const handleChange = (event, itemValue) => {
console.log(`id: ${event.target.value}, value: ${itemValue}`)
}
const test = [{id:1, name:'test', value:{x:10}}]
<Select
multiple
showSearch
style={{ width: 200 }}
placeholder="Select a person"
optionFilterProp="children"
onChange={(e) => this.handleChange(e, item.value)}
>
{test.map((item) =>(
<Option key={key} value={item.id}>{item.name}</Option>
))}
</Select>