score:6

Accepted answer

My solution was to wrap modal dialogue and form components in a new wrapper parent component in which I validate the child form component in handleCreate method. I have used the ref attribute to reference the myForm child component inside the FormOnModalWrapper component. I am passing the parent handlers via props from the wrapper parent component to myForm component instance.

class FormOnModalWrapper extends React.Component {
...
    constructor(props) {
        this.state =
        {
            visible: false
            ....
        }

...
    showModal = () => {
        this.setState({
            visible: true,
        });
    }

    handleCreate = () => {
        const form = this.form;
        form.validateFields((err, values) => {
            if (err) {
                return;
            }
            console.log('Received values of form: ', values);
            form.resetFields();
            this.setState({ visible: false });
        });
    }

    saveFormRef = (form) => {
        this.form = form;
    }

render() {
...
    const myForm= Form.create()(CrateNewItemFormOnModal);
...
    return (
      <div>
            <Button onClick={this.showModal}>Add</Button>
            <myForm
                visible={this.state.visible}
                onCancel={this.handleCancel}
                onCreate={this.handleCreate}
                ref={this.saveFormRef}
            />
      </div>
     );
}

In CrateNewItemFormOnModal component class I have a modal dialogue component as a parent and form component as a child:

export default class AddNewItemForm extends React.Component {

render() {
    ...
    const { visible, onCancel, onCreate, form } = this.props;
    ...
    return (
        <Modal
           title="Create new item"
           visible={visible}
           onOk={onCreate}
           onCancel={onCancel}
           okText="Create"
        >
          <Form>
            ...
          </Form>
        </Modal>
    );
}

score:0

My solution 1st solution

...
handleOk = (e) => {
   e.preventDefault();
   this.form.validateFields((err, values) => {
      //do your submit process here
   });
}
//set ref form
formRef = (form) => {
  this.form = form;
}
render() {
    const myForm = Form.create()(AddNewItemForm);
    ...
    return (
        ...
        <Modal
            title="Create new item"
            visible={this.state.visible}
            onOk={this.handleOk}
            onCancel={this.handleCancel}
            wrapClassName="vertical-center-modal"
            okText="Save new item"
            width="600"
        >
            <myForm
               ref={this.formRef}
            />
        </Modal>
...

or you can use this solution

...
handleOk = (e) => {
   e.preventDefault();
   this.form.validateFields((err, values) => {
      //do your submit process here
   });
}
render() {
    const myForm = Form.create()(AddNewItemForm);
    ...
    return (
        ...
        <Modal
            title="Create new item"
            visible={this.state.visible}
            onOk={this.handleOk}
            onCancel={this.handleCancel}
            wrapClassName="vertical-center-modal"
            okText="Save new item"
            width="600"
        >
            <myForm
               wrappedComponentRef={(form) => this.formRef = form}
            />
        </Modal>
...

The idea is to set the ref for wrapped the form component.

Please see the reference below.

Reference

score:0

Simple way to do this in 2021 is making customized footer for modal

import { useState } from 'react'
import { Modal, Button, Form, Input } from 'antd'

export default function BasicModal() {
    const [form] = Form.useForm()
    const [isModalVisible, setIsModalVisible] = useState(false)

    const showModal = () => setIsModalVisible(true)

    const handleCancel = () => {
        setIsModalVisible(false)
        form.resetFields()
    }

    const handleOk = () => {
        form.submit()
    }

    const onFinish = () => {
        console.log('Form submited!')
        setIsModalVisible(false)
    }

    return (
        <>
            <Button type="primary" onClick={showModal}>
                Show Modal
            </Button>

            <Modal
                title="Basic Modal"
                visible={isModalVisible}
                onOk={handleOk}
                onCancel={handleCancel}
                footer={[
                    <Button key="back" onClick={handleCancel}>
                        Cancel
                    </Button>,
                    <Button key="submit" type="primary" onClick={handleOk}>
                        Submit
                    </Button>,
                ]}
            >
                <Form labelCol={{ xs: { span: 6 } }} wrapperCol={{ xs: { span: 12 } }} form={form} onFinish={onFinish} scrollToFirstError>
                    <Form.Item name="input1" label="Input 1" rules={[{ required: true, message: "This field is required." }]}>
                        <Input />
                    </Form.Item>

                    <Form.Item name="input2" label="Input 2" rules={[{ required: true, message: "This field is required." }]}>
                        <Input />
                    </Form.Item>
                </Form>
            </Modal>
        </>
    )
}

score:1

My solution was to disable the modal's footer and create my own submit button:

<Modal footer={null}>
  <Form onSubmit={this.customSubmit}>
    ...
    <FormItem>
      <Button type="primary" htmlType="submit">Submit</Button>
    </FormItem>
  </Form>
</Modal>

No need to wrap the modal with this solution.

score:1

Now, react hooks are out you can achieve the same thing using hooks also. By creating a wrapper component for the modal and used that component where the form is.

Wrapper Component:

 <Modal
  visible={state}
  centered={true}
  onCancel={() => setState(false)}
  title={title}
  destroyOnClose={true}
  footer={footer}>
  {children}
</Modal>

Form Component:

   <WrapperModal
    state={modalState}
    setState={setModal}
    title='Example Form'
    footer={[
      <button onClick={handleSubmit}>
        SUBMIT
      <button/>
    ]}>
    <Form>
          <Form.Item label='name '>
            {getFieldDecorator('name ', {
              rules: [
                {
                  required: true,
                  message: 'please enter proper name'
                }
              ]
            })(<Input placeholder='name'/>)}
          </Form.Item>
    </Form>
  </WrapperModal>

here i had created a wrapper modal component which have all the necessary api for the modal, also i am creating a custom buttons for my modal

score:10

My solution is using hooks

import { Button,  Modal, Form } from 'antd';

export default function ModalWithFormExample() {
  const [visible, setVisible] = useState(false);
  const [form] = Form.useForm();
  
  const showModal = () => {
    setVisible(true)
  }

  const handleSubmit = (values) => {
    console.log(values)
  }
  
  const handleCancel = () => {
    setVisible(false)
    form.resetFields()
  };
  
  return (
    <>
      <Button onClick={showModal}>Open Modal</Button>
      <Modal visible={visible} onOk={form.submit} onCancel={handleCancel}>
        <Form form={form} onFinish={handleSubmit}>
          {/* Any input */}
        </Form>
      </Modal>
    </>
  )
}

score:40

There is a new solution that looks much cleaner:

<Form id="myForm">

...

<Modal
    ...
    footer={[
        <Button form="myForm" key="submit" htmlType="submit">
            Submit
        </Button>
        ]}
>
    <CustomForm />
</Modal>

This works because of the Button's form attribute. Browser support

Original solution's author: https://github.com/ant-design/ant-design/issues/9380


Related Query

More Query from same tag