Lashan Fernando
Lashan Fernando

Reputation: 1255

React antd form disable submit button

How could I disable form submit button using antd

Form validations work, but I need to visually disable form submit button when validation fails

enter image description here

This is a stackblitz url https://stackblitz.com/edit/react-ts-z5d6tr?file=Hello.tsx

Upvotes: 16

Views: 31237

Answers (8)

Aravin
Aravin

Reputation: 7067

I have handled this way

  1. create a state
const [formError, setFormError] = useState(false);
  1. create onChange function
  const onValuesChange = async (values: any) => {
    try {
      await form.validateFields();
      setFormError(false);
    } catch (err) {
      setFormError(true);
    }
  }
  1. attach onChange function to form
<Form onValuesChange={onValuesChange} > </Form>
  1. on the submit button
<Button type="primary" size="large" htmlType="submit" disabled={!form.isFieldsTouched() || formError}>Save</Button>

you can also use !form.isFieldsTouched(true)

Working example: https://github.com/Aravin/Invoice-Nest/blob/main/code/client/component/Invoice/InvoiceForm.tsx#L451

This worked for me!

Upvotes: 0

mahmud aslani
mahmud aslani

Reputation: 1

I have a good solution without any additional re-render(re-evaluated) cost!! I control button component with Form.Item because it passes the Value prop to the button and when the button props changed React re-evaluate that, so if we use this Value prop inside the button for disabling and enabling, the problem will be solved!! for example:

<Form
    name="form"
    form={form}
    initialValues={{
      button: 1,
    }}
    onFieldsChange={() => {
      // button field value = true if there isn't an error
      form.setFieldValue(
        "button",
       //Attention: put all fields except the button field
        !form.isFieldsTouched(["input"], true) ||
          form.getFieldsError().filter(({ errors }) => {
            return errors.length;
          }).length > 0
      );
    }}
  >
    <Form.Item
      name="input"
      rules={[
        {
          required: true,
        },
      ]}
    >
      <Input />
    </Form.Item>
    <Form.Item name="button">
      <CustomButton>login</CustomButton>
    </Form.Item>
  </Form>
function CustomButton({children, ...rest}) {
  return (
    <Button
      {...rest}
      disabled={rest.value}
    >
    {children}
   </Button>
  );

Upvotes: 0

Aravin
Aravin

Reputation: 7067

This solution works for me!

const [formError, setFormError] = useState(false);
...
...
const onValuesChange = async (values: any) => {
  try {
    await form.validateFields();
    setFormError(false);
  } catch (err) {
    setFormError(true);
  }
}
...
...
<Form form={form} onValuesChange={onValuesChange} />
...
...
<Form.Item label="First Name" name='firstName' required={true} rules={[{ required: true, min: 3, max: 50,
  message: 'First name is required, min 3 and max 50 characters!' }]}>
  <Input />
</Form.Item>
...
...
<Button disabled={formError}>Save</Button>
</Form>

Upvotes: 0

alextrastero
alextrastero

Reputation: 4280

I'm going to paste here my solution in case anyone finds it useful. Note; this solution uses the hook Form.useForm() and the callback onFieldsChange.

The validation is not included in this excerpt. But the idea is that when the form changes (any field) it will set the submit button (in this case inside the Modal) to disabled/enabled.

const LoginForm = () => {
  const [form] = useForm();
  const [disabledSave, setDisabledSave] = useState(true);
  
  const handleFormChange = () => {
    const hasErrors = form.getFieldsError().some(({ errors }) => errors.length);
    setDisabledSave(hasErrors);
  }
  
  return (
    <Modal okButtonProps={{ disabled: disabledSave }} onOk={form.submit}>
      <Form onFieldsChange={handleFormChange} form={form}>
        <Item name="username" label="username">
          <Input />
        </Item>
        <Item name="password" label="password">
          <Input />
        </Item>
      </Form>
    </Modal>
  )
};

Upvotes: 18

CB721
CB721

Reputation: 296

I found a nice solution here that doesn't involve any additional state. If you wrap your submit button in a Form.Item like this, it should disable it until all required fields are completed. The rest of the form would remain the same.

<Form.Item
 noStyle
 shouldUpdate
>
  {({ getFieldsValue }) => {
    const { username, password } = getFieldsValue();
    const formIsComplete = !!username && !!password;
    return (
      <Button
         type="primary"
         htmlType="submit"
         disabled={!formIsComplete}
       >
         Log In
       </Button>
    );
  }}
</Form.Item>

Upvotes: 8

giovaniZanetti
giovaniZanetti

Reputation: 472

import { UnlockOutlined, UserOutlined } from "@ant-design/icons";
import { Button, Card, Form, Input, Layout, Space } from "antd";
import { ValidateErrorEntity } from "rc-field-form/es/interface";
import React, { useState } from "react";
import "antd/dist/antd.css";

interface LoginForm {
  username: string;
  password: string;
}

const Hello = () => {
  // Create State
  const [error, setError] = useState(false);

  const onFinish = (values: LoginForm) => {
  };

  

  const onFinishFailed = (errorInfo: ValidateErrorEntity) => {
    setError(errorInfo); // set the state if error
  };

  // When the user starts type set state to false
  const handleChange = e => e.target.value && setError(false);

  return (
    <Layout className="login">
      <Card className="card">
        <Form
          onFinish={onFinish}
          onFinishFailed={onFinishFailed}
          size="large"
          layout="vertical"
        >
          <Space direction="vertical" size="middle">
            <Form.Item
              name="username"
              rules={[
                { required: true, message: "Please input your username!" }
              ]}
            >
              {/*Listening to change event*/ }
              <Input onChange={handleChange} prefix={<UserOutlined />} placeholder="Username" />
            </Form.Item>

            <Form.Item
              name="password"
              rules={[
                { required: true, message: "Please input your password!" }
              ]}
            >
              {/*Listening to change event*/ }
              <Input.Password
                onChange={handleChange}
                prefix={<UnlockOutlined />}
                placeholder="Password"
              />
            </Form.Item>

            <Form.Item className="submit">
              {/*Disable button only if error is true*/ }
              <Button disabled={error && true} type="primary" htmlType="submit">
                Login
              </Button>
            </Form.Item>
          </Space>
        </Form>
      </Card>
    </Layout>
  );
};

export default Hello;

Upvotes: 0

Lashan Fernando
Lashan Fernando

Reputation: 1255

I found some elegant way to perform this

<Form.Item shouldUpdate className="submit">
  {() => (
    <Button
      type="primary"
      htmlType="submit"
      disabled={
        !form.isFieldsTouched(true) ||
        form.getFieldsError().filter(({ errors }) => errors.length)
          .length > 0
      }
    >
      Log in
    </Button>
  )}
</Form.Item>

Upvotes: 27

VersifiXion
VersifiXion

Reputation: 2292

I did some edits here : https://stackblitz.com/edit/react-ts-spuhdd?file=Hello.tsx

Sum up of what I've done :

  1. I created hooks username, password and disabled
  2. Everytime the user changes username and password, if these fields are empty, disabled is set as false.
  3. If the onFinishFailed is executed, this disabled is set to true.
  4. When the disabled is at true, the button becomes disabled. If disabled property becomes false, the button becomes clickable again.

Upvotes: -1

Related Questions