Hema S
Hema S

Reputation: 169

How to dynamically disable the button of antd modal using button props

I have an antd Modal, i am trying to validate a field and provided validation to it. How can i enable/disable the Ok button based on the validation. If the validation is successful then button should be enabled else disabled.

<Form>
    <Modal
      title={modalHeader}
      okText="ADD FIELD"
      cancelText="CANCEL"
      visible={visible}
      onCancel={onCancelHandler}
      onOk={() => onAddFieldHandler(fieldName)}
      width={600}
      okButtonProps={{disabled:true}}
    >
      <p>Please provide name</p>
      <Form.Item
      name="fieldName"
      rules={[{ required: true, message: 'Please enter name' }]}
      >
      <FieldNameInput
        placeholder="Field name..."
        value={fieldName}
        onChange={(event) => setFieldName(event.target.value)}

      />
      </Form.Item>
    </Modal>
    </Form>

Upvotes: 3

Views: 16236

Answers (3)

frozzen10
frozzen10

Reputation: 161

In my case I had Form inside modal and there is onFieldChange prop when you can pass function to perform some operations due to changes on from so you can sth like that:

const SomeModal = ({ visible }) => {
  const [form] = Form.useForm();
  const [buttonDisabled, setButtonDisabled] = useState(true);

  const handleOk = () => form.submit();

  const handleAfterClose = () => { 
    setButtonDisabled(true);
    form.resetFields();
  }

  const handleCancel = () => ...some action to hide modal;

  const handleFormFinish = (values) => {
    ... some logic here
  }

  return (
    <Modal
      title={"Some title..."}
      visible={visibile}
      onOk={handleOk}
      onCancel={handleCancel}
      afterClose={handleAfterClose}
      okButtonProps={{ disabled: buttonDisabled }}
    >
      <Form
        form={form}
        layout="vertical"
        name="acceptform"
        onFinish={handleFormFinish}
        initialValues={{
          ...initial values here
        }}
        onFieldsChange={() => {
          const actualFieldValues = form.getFieldsValue();
          const anyError = form.getFieldsError().some((field) => field.errors.length > 0);
          .. some logic if error etc..
          if (anyError) {
             setButtonDisabled(true);
          } 
          else {
             setButtonDisabled(false);
          }
      }}
    >

and of course there is need to have some validators on fields

<Form.Item
    label={"someLabel"}
    id="field"
    name="field"
    hasFeedback
    rules={[
    {
        type: "string",
        validator: async (rule, value) => inputFieldValidate(value, "custom message")
    },
  ]}
>

and validator looks alike:

const inputFieldValidate = async (value, message) => {
  if (someCondition)) {
    return Promise.reject(message);
  }
  return Promise.resolve();
};

here is some nice to know that validator isasync and to make it work without any warnings just handle promises

Upvotes: 4

Having the Form inside the Modal, a way to update modal button status would be just running form instance's validateFields, but there are two things to take into account:

  1. This function is a Promise, so the state must update after an await with the validation results.

  2. I've experienced some looping issues when using onFieldsChange (maybe the validation triggers some kind of field update). Instead, onValuesChange has worked good enough for me.

  3. Running the validation into a setTimeout callback seems to be mandatory. Doing it without the setTimeout returns a validation error even when all the fields are valid because of an outOfDate: true. It seems to be because of how the Antd Form update lifecycle works, and waiting until this process has ended (what we can easily achieve with the setTimeout) solves that problem.

A succesful validation returns the form values object, a failed one returns an errorInfo object with the errors list, the outOfDate status and the current form values. You can use the errors list in the latter to get the validation messages returned by Antd to display more descriptive and specific feedback.

In the end, my approach has this structure:

const MyModal = ({onFinish, ...otherProps}) => {
  const [canSubmit, setCanSubmit] = useState(false);
  const [form] = Form.useForm();

  return (
    <Modal
      {...otherProps}
      okButtonProps={{
          disabled: !canSubmit
      }}
    >
      <MyFormComponent
        form={form}
        onFinish={onFinish}
        onValuesChange={() => {
          setTimeout(() => {
            form
              .validateFields()
              .then(() => {
                  /*
                  values:
                    {
                      username: 'username',
                      password: 'password',
                    }
                */
                setCanSubmit(true);
              })
              .catch((err) => {
                /*
                errorInfo:
                  {
                    values: {
                      username: 'username',
                      password: 'password',
                    },
                    errorFields: [
                      { name: ['password'], errors: ['Please input your Password!'] },
                    ],
                    outOfDate: false,
                  }
                */
                setCanSubmit(false);
              });
          });
        }}
      />
    </Modal>
  );
};

Upvotes: 1

zerocewl
zerocewl

Reputation: 12804

You can use onFieldsChange from Antd Forms API togehter with geFieldsError and the okButtonProps from Antd Modal API.

  const [form] = Form.useForm();
  const [buttonDisabled, setButtonDisabled] = useState(true);
  return (
    <Modal
      ...
      okButtonProps={{ disabled:  buttonDisabled  }}
    >
     <Form
        form={form}
        ...
        onFieldsChange={() =>
          setButtonDisabled(
            form.getFieldsError().some((field) => field.errors.length > 0)
          )
        }
      >

Here is a working Stackblitz.

Upvotes: 12

Related Questions