Dynamic Form with initial values - reactjs

Sorry for my english.
Like in this example
https://codesandbox.io/s/wonderful-lichterman-br63z?file=/index.js
Form.List is rendering the Array of "Fields" which initally is empty.
I would like to put my own array to be rendered from the start.
Expected result

you can do it using initialValues prop of Form.
Working example: https://codesandbox.io/s/bold-turing-g8ft6?file=/index.js
Docs: https://ant.design/components/form/#API

The docs are a bit short on explanation for the Form.List, and since a code sample is worth a thousand words...
This renders the dynamic form list with one item already visible:
const initialValues = {
users: [
{ age: undefined } // undefined will render the placeholder
]
};
<Form initialValues={initialValues}>
<Form.List name="users">
{(fields, { add }) => {
return (
<div>
{fields.map(field => (
<Row key={field.key}>
<Col>
<Form.Item
placeholder="age"
name={[field.age, 'age']}
>
<Input />
</Form.Item>
</Col>
<Col>
<Form.Item
placeholder="sex"
name={[field.sex, 'sex']}
>
<Input />
</Form.Item>
</Col>
<Col>
<Form.Item
placeholder="name"
name={[field.name, 'name']}
>
<Input />
</Form.Item>
</Col>
</Row>
))}
<button onClick={() => add()}>Add</button>
</div>
)
}}
</Form.List>
</Form>

Related

Antd dynamic form - how can I change form elements based on current values?

I have a dynamic Antd form where I can enter menu elements. The user can add, remove and reorder menu elements. The dynamic fields work fine, but I have a radio button for each dynamic element indicating that the corresponding menu element is an external URL or an internal page. I need to display different input elements if the user chooses URL or internal page. I tried the method listed in the Antd documentation, but it is for static fields and it doesn't seem to be working for dynamic elements.
My code so far is:
<Form.List name={"menuTree"}>
{(fields, { add, remove }) => (
<>
{fields.map((field, index) => (
<Row id={"menu-row-" + index} key={index}>
<Col span={8}>
<Form.Item label="Title" name={[field.name, "title"]} key={"title" + index + Math.random()}>
<Input />
</Form.Item>
</Col>
<Col span={4}>
<Form.Item label="Type" name={[field.name, "type"]}>
<Radio.Group>
<Radio value={"link"}>URL</Radio>
<Radio value={"page"}>Page</Radio>
</Radio.Group>
</Form.Item>
</Col>
<Col span={9}>
<Form.Item
// label="URL"
// name={[field.name, "url"]}
noStyle
shouldUpdate={(prevValues, currentValues) => prevValues.type !== currentValues.type}>
{({ getFieldValue }) => {
return getFieldValue("type") === "link" ? (
<Form.Item name={[field.name, "url"]} label="URL">
<Input />
</Form.Item>
) : (
<Form.Item name={[field.name, "page"]} label="Page">
<Input />
</Form.Item>
)
}}
<Input />
</Form.Item>
</Col>
<Col span={2}>
<Form.Item>
<Button htmlType="button" onClick={() => moveUp(index)}>
<CaretUpOutlined />
</Button>
<Button htmlType="button" onClick={() => moveDown(index)}>
<CaretDownOutlined />
</Button>
</Form.Item>
</Col>
<Col span={1}>
<Button htmlType="button" onClick={() => removeMenuItem(index)}>
<MinusOutlined />
</Button>
</Col>
</Row>
))}
<Button type="dashed" onClick={() => addMenuItem()} block icon={<PlusOutlined />}>
Add menu item
</Button>
</>
)}
</Form.List>
How can I achieve to change the input on a per line basis whenever the user changes the value of the radio button?
You are trying to get type value with wrong name path. getFieldValue expects the complete path when you want to get any value.
You form list name is menuTree. Since it's a list, your namepath will look like this:
getFieldValue(['menuTree', field.name, 'type'])
<Form>
<Form.List name={'menuTree'}>
{(fields, { add, remove }) => (
<>
{fields.map((field, index) => (
<Row id={'menu-row-' + index} key={index}>
<Col span={8}>
<Form.Item label='Title' name={[field.name, 'title']} key={'title' + index + Math.random()}>
<Input />
</Form.Item>
</Col>
<Col span={4}>
<Form.Item label='Type' name={[field.name, 'type']}>
<Radio.Group>
<Radio value={'link'}>URL</Radio>
<Radio value={'page'}>Page</Radio>
</Radio.Group>
</Form.Item>
</Col>
<Col span={9}>
<Form.Item
noStyle
shouldUpdate={(prevValues, currentValues) =>
prevValues?.menuTree?.[field.name]?.type !== currentValues?.menuTree?.[field.name]?.type
}
>
{({ getFieldValue }) => {
return (
<>
{getFieldValue(['menuTree', field.name, 'type']) === 'link' ? (
<Form.Item name={[field.name, 'url']} label='URL'>
<Input />
</Form.Item>
) : (
<Form.Item name={[field.name, 'page']} label='Page'>
<Input />
</Form.Item>
)}
</>
);
}}
</Form.Item>
</Col>
<Col span={2}>
<Form.Item>
<Button htmlType='button' onClick={() => {}}>
<CaretUpOutlined />
</Button>
<Button htmlType='button' onClick={() => {}}>
<CaretDownOutlined />
</Button>
</Form.Item>
</Col>
<Col span={1}>
<Button htmlType='button' onClick={() => remove(field.name)}>
<MinusOutlined />
</Button>
</Col>
</Row>
))}
<Button type='dashed' onClick={() => add()} block icon={<PlusOutlined />}>
Add menu item
</Button>
</>
)}
</Form.List>
</Form>

Populate react formik form with existing data

i am new to react, can anyone explain to me how can i manipulate or repopulate my existing form from backend data in react.
I am trying to edit and existing item in the inventory that i wanna change values for. i am using formik with react and formik grid. for data i am using AXIOS.
What i am trying to do is to get edit a specific entry from my database which has changed values. it's like trying to update values
<Formik
validationSchema={schema}
initialValues={{
name: "",
numberOfSets: "0",
sortValue: "0",
boxType: "",
price: "",
photo: "",
}}
onSubmit={({
name,
numberOfSets,
sortValue,
boxType,
price,
photo,
}) => {
boxService.addBox(
name,
numberOfSets,
sortValue,
boxType,
price,
photo
);
alert("Box added Successfully!"); // Box or Gift it's same thing
window.location.reload(false);
}}
>
{({
values,
errors,
touched,
handleBlur,
handleSubmit,
setFieldValue,
}) => {
return (
<Form
form={form}
name="edit-gift"
onFinish={handleSubmit}
{...layout}
labelAlign="left"
>
// These are the fields i am trying to manipulate
<Form.Item name="name" label="Name">
<Input
name="name"
title="Product Name"
dataIndex="name"
key="productName"
value={values.name}
onChange={(e) => setFieldValue("name", e.target.value)}
onBlur={handleBlur}
placeholder="Please enter Box Name"
/>
{errors?.name && touched?.name && (
<Text type="danger">{errors?.name}</Text>
)}
</Form.Item>
<Form.Item name="numberOfSets" label="Number of Sets">
<Input
name="numberOfSets"
type="number"
value={values.numberOfSets}
onChange={(e) =>
setFieldValue("numberOfSets", e.target.value)
}
onBlur={handleBlur}
placeholder="Please enter Number of Sets"
/>
{errors?.numberOfSets && touched?.numberOfSets && (
<Text type="danger">{errors?.numberOfSets}</Text>
)}
</Form.Item>
<Form.Item name="sortVlaue" label="Sort Value">
<Input
name="sortVlaue"
type="number"
value={values.sortValue}
onChange={(e) => setFieldValue("sortVlaue", e.target.value)}
onBlur={handleBlur}
placeholder="Please enter soring value"
/>
{errors?.numberOfBoxes && touched?.numberOfBoxes && (
<Text type="danger">{errors?.numberOfBoxes}</Text>
)}
</Form.Item>
<Form.Item name="boxType" label="Type">
<Select
value={values.boxType}
onChange={(value) => setFieldValue("boxType", value)}
onBlur={handleBlur}
placeholder="Please enter Box Type"
>
<Select.Option value="reward">Reward</Select.Option>
<Select.Option value="doublerandom">
Double Random
</Select.Option>
</Select>
{errors?.boxType && (
<Text type="danger">{errors?.boxType}</Text>
)}
</Form.Item>
<Form.Item name="price" label="Price">
<Input
name="price"
title="Product Price"
dataIndex="price"
key="price"
value={values.price}
onChange={(e) => setFieldValue("price", e.target.value)}
onBlur={handleBlur}
placeholder="Please enter Box Price"
/>
{errors?.price && touched?.price && (
<Text type="danger">{errors?.price}</Text>
)}
</Form.Item>
<Form.Item name="photo" label="Product Picture">
<div className="dropzone-container">
{values.photo && <img src={values.photo} alt=""></img>}
{!values.photo && (
<Dropzone
onDrop={(acceptedFiles) => {
acceptedFiles.map((file) => {
console.log(file);
});
let fr = new FileReader();
fr.onload = function () {
setFieldValue("photo", fr.result);
setFieldValue("imgName", acceptedFiles[0].name);
console.log(acceptedFiles[0]);
};
fr.readAsDataURL(acceptedFiles[0]);
}}
accept={{ "image/*": [] }}
maxSize={1000000}
>
{({ getRootProps, getInputProps }) => (
<section>
<div {...getRootProps({ style })}>
<input {...getInputProps()} />
<p>
Drag 'n' drop some files here, or click to
select files
</p>
<aside>
Tips: The size ratio of the photo is: 750:216,
the maximum recommended size is 1M, and the
format is: .jpg, .jpeg, .png
</aside>
</div>
</section>
)}
</Dropzone>
)}
</div>
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit">
Cofirm save
</Button>
</Form.Item>
</Form>
);
}}
</Formik>
You could either:
rerender the Formik component when your data is available and pass it to the initialValues prop by doing something like
results?.data && <Formik initialValues={{...}}>...</Formik>
or set the values manually with setValues after the data is available
const {setValues, values, ...} = useFormik({...});
useEffect(() => {
setValues(results?.data); // use any fields from your data
}, [results]} // this is the data from your api

how to set the state values in react input?

can you please help me how can i show value of state in input field and also input field is editable. i am trying many tricks but not helpful also can't find in Stackoverflow .......................................
I want show brandcode state property "BrandCode" value in input field................................
here is brandcode return values
brandcode : {
BrandCode: "Nick",
BrandID: 1
}
state = {
brandcode: [],
};
Getbrandcode(brandId) {
getBrandCode(brandId)
.then(res => {
debugger;
//console.log(res);
this.setState({
brandcode: res.data.Data // i want show brandcode values in input
});
});
}
``
its a render.....
``
render() {
// const { formikProps} = this.props
return (
<Formik
enableReinitialize={true}
initialValues={this.masterstyledata}
onSubmit={(values, actions) => {
this.onSaveClick(values);
}}
validationSchema={this.validationSchema}
>
{formikProps => (
<>
<Card>
<CardBody>
{this.state.isLoading && <LoadingPanel />}
<Row>
<Col lg={12}>
<Form>
<FormGroup row>
<Label for="Value" sm={3}>Brand</Label>
<Col sm={3}>
<ComboBoxValue
data={this.state.branddesc}
style={{ width: '100%' }}
name="BrandID"
textField="text"
valueField="Value"
dataItemKey="Value"
onChange={(e) => {
this.BrandCodehandleChange(e);
formikProps.setFieldValue("BrandID", e.target.value)
}}
value={formikProps.values.BrandID}
placeholder="Brand Description"
/>
</Col>
</FormGroup>
<FormGroup row>
//here i want set values
<Label for="BrandCode" sm={3}>Style ID</Label>
<Col sm={3}> <Input sm={3}
type="text"
name="BrandCode"
id="BrandCode"
onChange={formikProps.handleChange('MasterStyleCode')}
value={formikProps.values.MasterStyleCode}
placeholder="Enter Style Id"
/>
<Error>{formikProps.errors.MasterStyleCode}</Error>
</Col>
</FormGroup>
</Form>
</Col>
</Row>
</CardBody>
</Card>
you made a mistake in using handleOnChage and onChange, here:
onChange={formikProps.handleChange('MasterStyleCode')}
at render time this function will be executed and the result is undefined (as you know because it's a void function!), and it's not what you want here, to solve this you have to make a HOF and use setFieldValue if your input name is something different than initialValue key which you want to change it, e.g:
onChange={(event) => {
formikProps.setFieldValue('MasterStyleCode', event.target.value)
}}

input value to be erased in react-bootstrap-typeahead

using formik and react-bootstrap-typeahead, I am having 2 typeaheads inside formik
What i am trying to do is, I have 2 typeaheads, depending on the option selected from typeahead-1, I'm getting the options for the typeahead-2 and it works absolutely wonderful
<Formik
initialValues={{list:'',listOne:''}}
onSubmit={this.modalSubmit}
validationSchema={Yup.object().shape({
list: Yup.string().required("Select ID"),
listOne: Yup.string().required("Select ID"),
})}
>
{props=>{
const {
values,
touched,
errors,
dirty,
isSubmitting,
handleBlur,
handleSubmit,
handleReset,
setFieldValue,
setFieldTouched,
} = props
return(
<Form className="bootstrap" onSubmit={handleSubmit}>
<Form.Row className='row-form'>
<Form.Group as={Col} md={6} sm={6} lg={6} xl={6}>
<Form.Label>Client ID</Form.Label>
<div className='custom-dropdown'>
<i className="fal fa-angle-down fa-angle-assign"/>
<Typeahead
id='list'
name='list'
valueKey="id"
placeholder="Select ID"
options={listOptions}
onBlur={(e) => {setFieldTouched("list", true)}}
isValid={touched.list && !errors.list}
isInvalid={!!errors.list}
onChange={async selected=>{
if(selected[0]){
setFieldValue('list',selected)
await Options.loadListOneOptions(selected[0].id)
const getListOneOptions = Options.getListOneOptions
this.setState({
listOneOptions: getListOneOptions,
})
}
}}
/>
</div>
{errors.list && touched.list && (<div className="input-feedback">{errors.list}</div>)}
</Form.Group>
<Form.Group as={Col} md={6} sm={6} lg={6} xl={6}>
<Form.Label>Select ID</Form.Label>
<div className='custom-dropdown'>
<i className="fal fa-angle-down fa-angle-assign"/>
<Typeahead
id='listOne'
name='listOne'
valueKey="id"
placeholder="Select ID"
options={this.state.listOneOptions}
onBlur={(e) => {setFieldTouched("listOne", true)}}
isValid={touched.listOne && !errors.listOne}
isInvalid={!!errors.listOne}
onChange={...}
/>
</div>
{errors.listOne && touched.listOne && (<div className="input-feedback">{errors.listOne}</div>)}
</Form.Group>
</Form.Row>
<Form.Group style={{textAlign:'center'}} className='row-form'>
<Link to='...'>
<Button type='submit' id='cancelButtonStyle' style={{marginRight:'10px'}}>Cancel</Button>
</Link>
<Button type='submit' className="btn-default" style={{textTransform:'none',borderRadius:'0%'}}>Submit</Button>
</Form.Group>
</Form>
)
}}
</Formik>
Problem:-
how to erase the input value entered in search for typeahead-2 when different option is selected in typeahead-1
Scenario steps:
select option from typeahead-1, loads the options to typeahead-2
input some value in search bar and select an option from typeahead-2
go to typeahead-1 and select any other option and loads the options to typeahead-2,... but here the input value in search for typeahead-2 is still there as entered in step-2
i want know how the input value in search for typeahead-2 to be erased when different option is selected in typeahed-1
I got it resolved by using ref
// create a ref
this.typeaheadRef = React.createRef()
// add ref to typeahead-2
<Typeahead
ref={this.typeaheadRef}
...
/>
// use the ref in onChange of typeahaed-1, current.clear() will clear the input
<Typeahead
...
onChange={ selected=>{
...
this.typeaheadRef.current.clear()
...
}
}
/>
Hope this will help others

When i change another form element autocomplete value automatically cleared in Formik React js

The form has two elements.
When I select autocomplete value from emal element and then try to input a text value to titl field, the autocomplete value is automatically changed to empty.
What should I do to fix this issue?
I have tried to change formik initialValues using states but it's not working.
sorry for the language issue. Thanks in advance!
class TicketNew extends React.Component{
state = {
clearForm:false,
spinner:false,
closeForm:false,
emailsugges:[],
}
loadAlldata() {
this.setState({
spinner:false,
})
axios.post(baseUrl+'/api/load_company_list')
.then(res => {
const comanyList = res.data;
const emls = comanyList.emls.map(function(item, i){
return {
value:item.tci, title:item.tcc
}
})
this.setState({
emailsugges:emls
})
})
this.setState({
spinner:false,
})
};
componentDidMount(){
this.loadAlldata();
};
render(){
return(
<React.Fragment>
<Formik
initialValues={{ emal: "", titl: "" }}
validationSchema={formSchema}
>
{
({ errors,
touched,
handleSubmit,
isSubmitting,
handleBlur,
values,
resetForm
}) => (
<div>
<Form onSubmit={handleSubmit}>
<Card>
<CardHeader></CardHeader>
<CardBody>
<Row>
<Col md="5" sm="12">
<FormGroup row className="position-relative">
<Col md="4">
<span>Title</span>
</Col>
<Col md="8">
<Field
type="text"
name="titl"
id="titl"
className={`
form-control ${errors.titl && touched.titl && "is-invalid"}
`}
onBlur={handleBlur('titl')}
/>
{errors.titl &&
touched.titl ? (
<div className="invalid-tooltip mt-25">
{errors.titl}
</div>
) : null}
</Col>
</FormGroup>
</Col>
<Col md="2" sm="12"></Col>
<Col md="5" sm="12">
<FormGroup row className="position-relative"
style={{display:rqst!="1"?'none':''}}
>
<Col md="4">
<span>Email Address</span>
</Col>
<Col md="8">
<Field name="emal"
component={ ({field, form}) =>
<AutoComplete
type="email"
name="emal"
id="emal"
suggestions={this.state.emailsugges}
value={
this.state.emailsugges ?
this.state.emailsugges.find(option =>
option.value === field.value)
: ''}
className={`
form-control ${errors.emal && touched.emal && "is-invalid"}
`}
filterKey="title"
suggestionLimit={4}
/>}
/>
{errors.emal &&
touched.emal ? (
<div className="invalid-tooltip mt-25">
{errors.emal}
</div>
) : null}
</Col>
</FormGroup>
</Col>
</Row>
</CardBody>
</Card>
</Form>
</div>
)}
</Formik>
</React.Fragment>
)
}
};
export default TicketNew;

Resources