I have a dynamic form where users can add data. By default i want to set the form in this way:
How you can see, the first pair of input is open when user opens the application.
I made this by changing this:
const Demo = () => {
const onFinish = values => {
console.log("Received values of form:", values);
};
return (
<Form name="dynamic_form_nest_item" onFinish={onFinish} autoComplete="off">
<Form.List name="users">
{(fields, { add, remove }) => {
console.log(fields);
return (
<div>
{fields.map(field => (
<Space
key={field.key}
style={{ display: "flex", marginBottom: 8 }}
align="start"
>
<Form.Item
{...field}
name={[field.name, "first"]}
fieldKey={[field.fieldKey, "first"]}
rules={[{ required: true, message: "Missing first name" }]}
>
<Input placeholder="First Name" />
</Form.Item>
<Form.Item
{...field}
name={[field.name, "last"]}
fieldKey={[field.fieldKey, "last"]}
rules={[{ required: true, message: "Missing last name" }]}
>
<Input placeholder="Last Name" />
</Form.Item>
<MinusCircleOutlined
onClick={() => {
remove(field.name);
}}
/>
</Space>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
block
>
<PlusOutlined /> Add field
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
<Form.Item>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
);
};
to this:
const Demo = () => {
const onFinish = values => {
console.log("Received values of form:", values);
};
const firstDefaultOpen = {
name: 0,
key: 0,
isListField: true,
fieldKey: 0
};
return (
<Form name="dynamic_form_nest_item" onFinish={onFinish} autoComplete="off">
<Form.List name="users">
{(fields, { add, remove }) => {
console.log(fields);
return (
<div>
{fields.concat(firstDefaultOpen).map(field => (
<Space
key={field.key}
style={{ display: "flex", marginBottom: 8 }}
align="start"
>
<Form.Item
{...field}
name={[field.name, "first"]}
fieldKey={[field.fieldKey, "first"]}
rules={[{ required: true, message: "Missing first name" }]}
>
<Input placeholder="First Name" />
</Form.Item>
<Form.Item
{...field}
name={[field.name, "last"]}
fieldKey={[field.fieldKey, "last"]}
rules={[{ required: true, message: "Missing last name" }]}
>
<Input placeholder="Last Name" />
</Form.Item>
<MinusCircleOutlined
onClick={() => {
remove(field.name);
}}
/>
</Space>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
block
>
<PlusOutlined /> Add field
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
<Form.Item>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
);
};
Issue: But appears a problem when i try to add another input, because writing on first pair of inputs i can see the same text on the next inputs. Probably this is hapenening because of the same ids.
Question: How to set default open first pair of input as in the image using the code above?
demo: https://codesandbox.io/s/hungry-star-nu5ld?file=/index.js:235-2322
ps: i know that exists the next solution:
<Form initialValues={{ users: [""] }} name="dynamic_form_nest_item" onFinish={onFinish} autoComplete="off">
<Form.List name="users">
{(fields, { add, remove }) => {
console.log(fields);
return (
<div>
{fields.map(field => (
...
...
But i don't want this solution because i can't interact with <Form/> tag in my real application, so even this could be a solution, but i need another, like mine with concat() but of course to be workable.
Related
Warning message => react-dom.development.js:88 Warning: Encountered two children with the same key, 0. Keys should be unique so that components maintain their identity across updates. Non-unique keys may cause children to be duplicated and/or omitted — the behavior is unsupported and could change in a future version.
<Form name="dynamic_form_nest_item" onFinish={onFinish} autoComplete="off" >
<Form.List name="users" initialValue={[{ccn: "",cn:"",title:"" ,major: "",year:""}]}>
{(fields, { add, remove }) => (
<>
{fields.map(field => (
<Space key={field.key} style={{ display: 'flex', marginBottom: 8 }} align="baseline">
<div>
<Row>
<Form.Item
{...field}
name={[field.name, 'ccn']}
fieldKey={[field.fieldKey, 'ccn']}
rules={[{ required: true, message: 'Missing ccn' }]}
>
<AutoComplete
options={cocOptions}
style={{
width: 200,
}}
onSelect={oncocSelect}
onSearch={oncocSearch}
placeholder="Country of College/University"
/>
</Form.Item>
<Form.Item
{...field}
name={[field.name, 'cn']}
fieldKey={[field.fieldKey, 'cn']}
rules={[{ required: true, message: 'Missing cn name' }]}
>
<AutoComplete
options={cnOptions}
style={{
width: 200,
}}
onSelect={oncnSelect}
onSearch={oncnSearch}
placeholder="College/University Name"
/>
</Form.Item>
</Row>
<Row>
<Form.Item
{...field}
name={[field.name, 'title']}
fieldKey={[field.fieldKey, 'title']}
rules={[{ required: true, message: 'Missing title name' }]}
>
<Input placeholder="title Name" />
</Form.Item>
<Form.Item
{...field}
name={[field.name, 'major']}
fieldKey={[field.fieldKey, 'major']}
rules={[{ required: true, message: 'Missing major name' }]}
>
<AutoComplete
options={mOptions}
style={{
width: 200,
}}
onSelect={onmSelect}
onSearch={onmSearch}
placeholder="Major"
/>
</Form.Item>
<Form.Item
{...field}
name={[field.name, 'year']}
fieldKey={[field.fieldKey, 'year']}
rules={[{ required: true, message: 'Missing year name' }]}
>
<Select placeholder="Year" style={{ width: 120 }} onChange={handleChange}>
<Option value="jack">Jack</Option>
<Option value="lucy">Lucy</Option>
<Option value="Yiminghe">yiminghe</Option>
</Select>
</Form.Item>
<MinusCircleOutlined onClick={() => remove(field.name)} />
</Row>
</div>
</Space>
))}
<Form.Item>
<Button type="dashed" onClick={() => add()} block icon={<PlusOutlined />}>
Add field
</Button>
</Form.Item>
</>
)}
</Form.List>
<Form.Item hidden>
<Button ref={myButton} type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
how I can add my own fields to fill it in the form list
<Form.List
name="products"
>
{(data, { add, remove }) => {
console.log(data);
return (
<>
{data.map((product) => (
<>
<Col span={3} offset={2}>
<Form.Item
name={['referralNumber']}
rules={[{ required: true, message: 'Missing first name' }]}
>
<Input />
</Form.Item>
</Col>
<Col span={1} offset={1}>
<DeleteOutlined onClick={() => remove(product.id)} />
</Col>
<Divider />
</>
))}
<Col span={24}>
<Form.Item>
<Button type="dashed" onClick={add} block icon={<PlusOutlined />}>
افزودن کالا
</Button>
</Form.Item>
</Col>
</>
);
}}
</Form.List>
i wnat to have access to filed like this interface
export interface CreateReceiptItemDto {
receiptId: number;
productId: number;
initialMainQuantity: number;
initialSubQuantity: number;
referralNumber: string;
}
but i always get something like this and its defin in node_modules like this
import * as React from 'react';
import { ValidatorRule, StoreValue } from 'rc-field-form/lib/interface';
export interface FormListFieldData {
test: number;
test2: number;
fieldKey: number;
}
export interface FormListOperation {
add: (defaultValue?: StoreValue, insertIndex?: number) => void;
remove: (index: number | number[]) => void;
move: (from: number, to: number) => void;
}
export interface FormListProps {
prefixCls?: string;
name: string | number | (string | number)[];
rules?: ValidatorRule[];
initialValue?: any[];
children: (fields: FormListFieldData[], operation: FormListOperation, meta: {
errors: React.ReactNode[];
}) => React.ReactNode;
}
declare const FormList: React.FC<FormListProps>;
export default FormList;
i think i need something to overwrite this definition or something like that to customize it
i fond this way to create an array of object by my own model by iterate a form with my name then filed name in that's form and submit i get my model as an array
<Form.List
name="products"
initialValue={[{ id: 1, name: 'test', mainUnit: '2213', subUnit: '21312' }]}
>
{(data, { add, remove }) => {
console.log(data);
return (
<>
{data.map((product) => (
<>
<Form name="test">
<Col span={2}>
<Form.Item
name={['id']}
rules={[{ required: true, message: 'Missing first name' }]}
>
<Input />
</Form.Item>
</Col>
<Col span={5} offset={2}>
<Form.Item
name={'name'}
rules={[{ required: true, message: 'Missing first name' }]}
>
<Input />
</Form.Item>
</Col>
<Col span={2} offset={2}>
<Form.Item
name={'mainUnit'}
fieldKey={'mainUnit'}
rules={[{ required: true, message: 'Missing first name' }]}
>
<Input />
</Form.Item>
</Col>
<Col span={2} offset={2}>
<Form.Item
name={'subUnit'}
rules={[{ required: true, message: 'Missing first name' }]}
>
<Input />
</Form.Item>
</Col>
<Col span={3} offset={2}>
<Form.Item
name={['referralNumber']}
rules={[{ required: true, message: 'Missing first name' }]}
>
<Input />
</Form.Item>
</Col>
<Col span={1} offset={1}>
<DeleteOutlined onClick={() => remove(product.id)} />
</Col>
<Divider />
</Form>
</>
))}
<Col span={24}>
<Form.Item>
<Button type="dashed" onClick={add} block icon={<PlusOutlined />}>
افزودن کالا
</Button>
</Form.Item>
</Col>
</>
);
}}
</Form.List>
You can set values with initialValue props.
InitialValue props will set your fields defaultValue.
A Multiple value can set below code example:
<Form.List
name="products"
initialValue={[{ id: 5, name: 'example', mainUnit: '77', subUnit: '777' }]}
>
A single value can set below code example:
<Form.Item
name={['id']}
initialValue="5"
rules={[{ required: true, message: 'Missing first name' }]}
>
You can try with your preference values with single or multiple.
I am having trouble with unit snapshot testing and cant figure why.
I am getting the following error:
enter image description here
The component is actually a Form with 3 fields: Full name, Email and Note. There is also a checkbox which controls the Email and Note field in case we dont want to put that information in the form.
For the Note I am using TextareaAutosize, so that the text area is re sizable (due to requirements).
const [missingInfo, setMissingInfo] = useState(false);
const initialValues = {
fullName: '',
email: '',
note: '',
};
return (
<Formik
initialValues={initialValues}
validationSchema={object({
fullName: string().required('Must contain a full name'),
})}
onSubmit={(values, { setSubmitting }) => {
setSubmitting(true);
...
}}
>
<Form>
<Typography>Title</Typography>
<HashLink
to={{ hash: '#xxx' }}
onClick={() => toggleHash()}
>
Changed your mind?
</HashLink>
<Typography>Full name</Typography>
<Field name="fullName">
{({ field }: FieldProps) => (
<TextField
id="fullName"
variant="outlined"
type="text"
fullWidth
{...field}
/>
)}
</Field>
<ErrorMessage name="fullName">
{msg => <div className={styles.errorMessage}>{msg}</div>}
</ErrorMessage>
<FormControlLabel
control={
<Checkbox
checked={missingInfo}
onChange={() => setMissingInfo(!missingInfo)}
name="missingInfo"
/>
}
label="No info?"
/>
{!missingInfo && (
<>
<Typography>Email</Typography>
<Field name="email">
{({ field }: FieldProps) => (
<TextField
id="email"
variant="outlined"
type="text"
fullWidth
{...field}
/>
)}
</Field>
<Typography>Note</Typography>
<Field name="note">
{({ field }: FieldProps) => (
<TextareaAutosize
rowsMin={3}
className={styles.textArea}
{...field}
/>
)}
</Field>
</>
)}
<Button
variant="contained"
type="submit"
>
Apply
</Button>
</Form>
</Formik>
)
The error is as I said in the snapshot test:
test('matches snapshot', () => {
const tree = renderer
.create(
<BrowserRouter>
<MyComponent {...props} />
</BrowserRouter>,
)
.toJSON();
expect(tree).toMatchSnapshot();
});
Can someone please help?
I have the next application:
https://codesandbox.io/s/suspicious-murdock-87j13?file=/SubForm.js
<Form.List name="names">
{(fields, { add, remove }) => {
return (
<div>
{fields.map((field, index) => {
console.log("m", field);
return (
<Form.Item
{...(index === 0
? formItemLayout
: formItemLayoutWithOutLabel)}
label={index === 0 ? "Passengers" : ""}
required={false}
key={index}
>
<Form.Item
{...field}
name={[field.name, "outer"]}
fieldKey={[field.fieldKey, "outer"]}
validateTrigger={["onChange", "onBlur"]}
rules={[
{
required: true,
whitespace: true,
message:
"Please input passenger's name or delete this field."
}
]}
noStyle
>
<Input
placeholder="passenger name"
style={{ width: "60%" }}
/>
</Form.Item>
<Form.Item
{...field}
name={[field.name, "outer1"]}
fieldKey={[field.fieldKey, "outer1"]}
validateTrigger={["onChange", "onBlur"]}
rules={[
{
required: true,
whitespace: true,
message:
"Please input passenger's name or delete this field."
}
]}
noStyle
>
<Input
placeholder="passenger name"
style={{ width: "60%" }}
/>
</Form.Item>
<Form.Item>
<InnerForm fieldKey={field.key} />
</Form.Item>
{fields.length > 1 ? (
<MinusCircleOutlined
className="dynamic-delete-button"
style={{ margin: "0 8px" }}
onClick={() => {
remove(field.name);
}}
/>
) : null}
</Form.Item>
);
})}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
style={{ width: "60%" }}
>
<PlusOutlined /> Add field
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
<Form.Item>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
When user click on Add field button, in console appears the warning:
Warning: Encountered two children with the same key, `0`. Keys should be unique so that components maintain their identity across updates. Non-unique keys may cause children to be duplicated and/or omitted — the behavior is unsupported and could change in a future version.
Also this warning appears when i click on Add sub-field. I can't figure out why this warning appears.
Question: Why the above warning appears and how to remove it?
You have two Form.Item Component in your code, and they use same key index, try to use different key like 'item1' + index and 'item2' + index.
And, in your index.js file also have same problem.
Here is Demo forked from you: https://codesandbox.io/s/dark-darkness-xz0wf?file=/SubForm.js
As reactJs primary works on re rendering items every time you make a change the state of that component. It requires an unique key to track each and every item so that re-rendering can be optimized. Not all items needs to be re-render every time.
This warning essentially means that you have multiple items with same key names. In order to fix this you have to provide unique "key" attribute to each child component which is getting updated, components which can potentially trigger an re-render.
you don't need to use multiple times key={index} or key={"g"}
use once only.
Please try following
import React from 'react';
import ReactDOM from 'react-dom';
import 'antd/dist/antd.css';
import './index.css';
import { Form, Input, Button, Space } from 'antd';
import { MinusCircleOutlined, PlusOutlined } from '#ant-design/icons';
const Demo = () => {
const onFinish = values => {
console.log('Received values of form:', values);
};
return (
<Form name="dynamic_form_nest_item" onFinish={onFinish} autoComplete="off">
<Form.List name="users">
{(fields, { add, remove }) => {
return (
<div>
{fields.map(field => (
<Space key={field.key} style={{ display: 'flex', marginBottom: 8 }} align="start">
<Form.Item
{...field}
name={[field.name, 'innerName']}
fieldKey={[field.fieldKey, 'innerName']}
>
<Input placeholder="passenger 1" />
</Form.Item>
<Form.Item
{...field}
name={[field.name, 'innerName2']}
fieldKey={[field.fieldKey, 'innerName2']}
>
<Input placeholder="passenger 2" />
</Form.Item>
<MinusCircleOutlined
onClick={() => {
remove(field.name);
}}
/>
</Space>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
block
>
<PlusOutlined /> Add field
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
<Form.Item>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
);
};
ReactDOM.render(<Demo />, document.getElementById('container'));
I have a problem with the extraction of my input values onChange in my higher order component.
I want to assign input values to my contactFormData objectc properties.
Code:
export default withReflex()(ContactForm)
function ContactForm() {
let contactFormData = {
subject: '',
email: '',
message: '',
}
return (
<Section style={styles.container}>
<SectionTitle>
Contact Us
</SectionTitle>
<SectionSubtitle>
And let's code together
</SectionSubtitle>
<Flex col={12} justify="center" wrap style={{ margin: 'auto' }}>
<Flex p={2} col={12} md={6}>
<input
style={styles.input}
type="text" name="subject"
placeholder="Subject"
onChange={(subjectInput) => contactFormData.subject = subjectInput}
required
/>
</Flex>
<Flex p={2} col={12} md={6}>
<input
style={styles.input}
type="email"
name="from"
placeholder="Email"
onChange={(emailInput) => contactFormData.email = emailInput}
required />
</Flex>
<Flex p={2} col={12}>
<textarea
style={{ ...styles.input, height: 250 }}
placeholder="Message"
name="body"
onChange={(messageInput) => contactFormData.message = messageInput}
required
/>
</Flex>
<Flex p={2} col={12} justify="center">
<Button
typeStyle="primary"
style={{ width: '100%' }}
onClick={sendContactMail}
>
SEND THE MESSAGE
</Button>
</Flex>
</Flex>
</Section>
)
function sendContactMail() {
console.log(contactFormData);
}
}
When i console.log my object all values are "proxy" insted of expected values
Console:
use
onChange={(event) => contactFormData.subject = event.target.value;