Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use Antd InternalFormInstance to validate form without display of UI Errors

I am trying to disable the form submit button until the validation is fully passed.

I have come across the threads regarding this topic.

This thread helps to trigger validation without display of UI errors: https://github.com/ant-design/ant-design/issues/25993

The below code works within the hoc of my footer button wrapper, but it is verifying for all fields to be touched and even applicable for non-required fields, which is not right and expected.

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

Unfortunately, https://github.com/ant-design/ant-design/issues/23281 thread is full of Chinese and I cannot understand it.

My existing form.validateFields refers to formInstance, but it is referring to: InternalFormInstance.

How to import this and validate? Is it really supported in antd 4 version?

CodeSandbox link: https://codesandbox.io/s/gallant-merkle-21izz?file=/src/withFormWrapperHOC.tsx

Example code as a reference will be helpful!

The form validation miserably fails when select list is included. Form onChange or onChangeValues don't work; when we dig more the !form.isFieldsTouched(true) is always true even when there is no validation rule associated with Select List.

Ref ticket: https://github.com/ant-design/ant-design/issues/8436

It looks like Antd has some open issues with Rc-select api integrations and also which is not exposed.

Should we really consider Antd or any other form validation?

like image 619
Mithun Shreevatsa Avatar asked Sep 29 '21 15:09

Mithun Shreevatsa


Video Answer


1 Answers

For disabling validation on first render you can use Ref workaround

const isMounting = React.useRef(false);
useEffect(() => {
  if (isMounting.current) {
    const disableStatus = !!form
      .getFieldsError()
      .filter(({ errors }) => errors.length).length;
    setIsDisabled(disableStatus);
  }
  else {
    isMounting.current = true
  }
}, [form]);

Another option would to disable login button till all the form inputs are filled and then validate with Login

import React, { useState } from "react";
import { Button, Form, Select } from "antd";
import "antd/dist/antd.css";

const { Option } = Select;

export function withFormWrapper(WrappedComponent: any) {
  return (props: any) => {
    const [form] = Form.useForm();
    const [isDisabled, setIsDisabled] = useState(true);

    function fieldIsEmpty(field) {
      let fieldValue = form.getFieldValue(field.name.join("."));
      return (
        fieldValue === undefined || [].concat(fieldValue).join().trim() === ""
      );
    }

    function fieldHasError(field) {
      return field.errors.length > 0;
    }

    function isValid() {
      const fields = form
        .getFieldsError()
        .filter((field) => fieldIsEmpty(field) || fieldHasError(field));

      console.log(fields);
      setIsDisabled(fields.length > 0);
    }

    const validate = () => {
      form
        .validateFields()
        .then((values: any) => {
          console.log("success");
        })
        .catch((errorInfo: any) => {
          console.log("failure");
        });
    };

    return (
      <Form form={form} onChange={isValid}>
        <WrappedComponent {...props} />
        <Form.Item name="gender" label="Gender" rules={[{ required: true }]}>
          <Select
            placeholder="Select a option and change input text above"
            onChange={isValid}
            allowClear
          >
            <Option value="male">male</Option>
            <Option value="female">female</Option>
            <Option value="other">other</Option>
          </Select>
        </Form.Item>
        <Form.Item>
          <Button
            type="default"
            htmlType="submit"
            onClick={() => form.resetFields()}
          >
            Cancel
          </Button>
        </Form.Item>

        <Form.Item shouldUpdate>
          {() => (
            <Button
              onClick={validate}
              type="primary"
              htmlType="submit"
              disabled={isDisabled}
            >
              Log in
            </Button>
          )}
        </Form.Item>
      </Form>
    );
  };
}

Here working example


With Form.Provider onFormChange validation can be fired for Select Component

import React, { useState } from "react";
import { Button, Form, Select } from "antd";
import "antd/dist/antd.css";

const { Option } = Select;

export function withFormWrapper(WrappedComponent: any) {
  return (props: any) => {
    const [form] = Form.useForm();
    const [isDisabled, setIsDisabled] = useState(true);

    function fieldIsEmpty(field) {
      let fieldValue = form.getFieldValue(field.name.join("."));
      return (
        fieldValue === undefined || [].concat(fieldValue).join().trim() === ""
      );
    }

    function fieldHasError(field) {
      return field.errors.length > 0;
    }

    function isValid() {
      const fields = form
        .getFieldsError()
        .filter((field) => fieldIsEmpty(field) || fieldHasError(field));

      setIsDisabled(fields.length > 0);
    }

    const validate = () => {
      form
        .validateFields()
        .then((values: any) => {
          console.log("success");
        })
        .catch((errorInfo: any) => {
          console.log("failure");
        });
    };

    return (
      <Form.Provider onFormChange={isValid}>
        <Form form={form}>
          <WrappedComponent {...props} />
          <Form.Item
            name="gender"
            label="Gender"
            rules={[{ required: true }]}
            shouldUpdate
          >
            <Select
              placeholder="Select a option and change input text above"
              allowClear
            >
              <Option value="male">male</Option>
              <Option value="female">female</Option>
              <Option value="other">other</Option>
            </Select>
          </Form.Item>
          <Form.Item name="Tags" label="Tags" shouldUpdate>
            <Select
              mode="tags"
              style={{ width: "100%" }}
              placeholder="Tags Mode"
              onChange={handleChange}
            >
              {children}
            </Select>
          </Form.Item>
          <Form.Item>
            <Button
              type="default"
              htmlType="submit"
              onClick={() => form.resetFields()}
            >
              Cancel
            </Button>
          </Form.Item>

          <Form.Item shouldUpdate>
            {() => (
              <Button
                onClick={validate}
                type="primary"
                htmlType="submit"
                disabled={isDisabled}
              >
                Log in
              </Button>
            )}
          </Form.Item>
        </Form>
      </Form.Provider>
    );
  };
}

Here working example

like image 60
Chandan Avatar answered Oct 01 '22 23:10

Chandan