'Antd Creating Dynamic Form Item inside table

Hello I am trying to achieve this code. where in I am creating new purchase order. I add items to table using its unique barcode. Whenever I add an item. it should also create a dynamic form item field that contains InputNumber. Upon submission all items with desired quantity should be submitted.

I am confused on how should I implement this. Any insights will do. the documentation is kinda limited on this.

Here is how I implemented this but it is not working.

const columns = [
    {title: 'ID',dataIndex: 'barcode',sorter: true,width: 'auto'},
    {title: 'Product',dataIndex: 'name',sorter: true,width: 'auto'},
    {title: 'Brand',dataIndex: 'brand',sorter: true,width: 'auto'},
    {title: 'Quantity',dataIndex:'quantity_order',sorter: true,width: 'auto', editable:true,
    render:(text,record,index)=>(
        <Col>
            <Form.Item
            name="record.quantity_order"
            rules={[{ required: true, message: 'Please input a quantity' }]}
            >

            <InputNumber/>
            </Form.Item>
        </Col>
    )},
    {title: 'Price',dataIndex: 'unit_price',width: 'auto'},
    {title: 'Action',width: 'auto',render:(text,record)=>(
        <Col>
        <span style={{marginRight:"0.5rem"}}>
            <Button shape="circle" icon={<EyeOutlined/>} size={'large'} onClick={()=>{}}/>
        </span>
        <span>
            <Button shape="circle" icon={<DeleteOutlined/>} size={'large'} onClick={()=>{}}/>
        </span>
        </Col>
    )}
];

Also tried with the dynamic form implementation. but It cannot be positioned inside column render function.

enter image description here



Solution 1:[1]

I use two forms. One for adding barCode and second form, i wrapped around the table. I used immer library for immutable state. You can replace it with any other package or add your own code to update.

import produce from 'immer';
import { Button, Col, Form, Input, InputNumber, Table, TableColumnType } from 'antd';
import { DeleteOutlined, EyeOutlined } from '@ant-design/icons';
import { useState } from 'react';

let index = 1;

export default function App() {
    const [form] = Form.useForm();
    const [tableForm] = Form.useForm();
    const [data, setData] = useState<any[]>([]);

    const columns: TableColumnType<any>[] = [
        { title: 'ID', dataIndex: 'barcode', sorter: true, width: 'auto' },
        { title: 'Product', dataIndex: 'name', sorter: true, width: 'auto' },
        { title: 'Brand', dataIndex: 'brand', sorter: true, width: 'auto' },
        {
            title: 'Quantity',
            dataIndex: 'quantity_order',
            sorter: true,
            width: 'auto',
            render: (text, record, index) => (
                <Col>
                    <Form.Item name={['record.quantity_order', index]} rules={[{ required: true, message: 'Please input a quantity' }]}>
                        <InputNumber
                            onChange={(e) => {
                                setData(
                                    produce((draft) => {
                                        draft[index].quantity_order = e;
                                    })
                                );
                            }}
                        />
                    </Form.Item>
                </Col>
            )
        },
        { title: 'Price', dataIndex: 'unit_price', width: 'auto' },
        {
            title: 'Action',
            width: 'auto',
            render: (text, record) => (
                <Col>
                    <span style={{ marginRight: '0.5rem' }}>
                        <Button shape='circle' icon={<EyeOutlined />} size={'large'} onClick={() => {}} />
                    </span>
                    <span>
                        <Button shape='circle' icon={<DeleteOutlined />} size={'large'} onClick={() => {}} />
                    </span>
                </Col>
            )
        }
    ];

    const onFinish = (formData: any) => {
        let barcode = data.find((val) => val.barcode === formData.barcode);
        if (!barcode) {
            setData(
                produce((draft) => {
                    draft.push({
                        barcode: formData.barcode,
                        name: `Product Name ${index}`,
                        brand: `Brand ${index}`,
                        quantity_order: 0,
                        unit_price: 0
                    });
                })
            );
            index++;
        }
        form.resetFields();
    };

    const onSubmit = () => {
        tableForm.validateFields().then(() => {
            console.log('Data:', data);
        });
    };

    return (
        <>
            <Form form={form} onFinish={onFinish}>
                <Form.Item name='barcode' rules={[{ required: true }]}>
                    <Input
                        addonAfter={
                            <Button type='primary' htmlType='submit'>
                                Add
                            </Button>
                        }
                    />
                </Form.Item>
            </Form>
            <Form form={tableForm} onFinish={onSubmit}>
                <Table dataSource={data} columns={columns} />
                <Button htmlType='submit'>Submit</Button>
            </Form>
        </>
    );
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Nouman Rafique