reactjs Antd - validateFields仅检查当前表单步骤

i7uaboj4  于 2023-02-22  发布在  React
关注(0)|答案(2)|浏览(422)

我正在尝试让表单步骤易于导航。必须允许用户通过单击以前的表单步骤返回,如果目标步骤和中间的所有内容都已填写且有效,则前进。
我通过使用类似这样的方法使它工作起来,但这个方法的问题是validateFields()只检查当前步骤的表单,所以我可以填写,比方说第一步,然后向前跳8步,因为validateFields()只验证当前步骤,认为一切都很好。

form.validateFields()
      .then(() => {
        setCurrentStep(step);
      })
      .catch((err) => {
        console.log(err);
        return;
      });

所以我尝试让validateFields()接受一个包含需要检查的每个表单字段名的数组。但是,我找不到任何关于这种实现的文档,下面的文档总是导致解析。

const handleStepChange = (step) => {
    // let user go back to previous steps
    if (step <= currentStep) {
      setCurrentStep(step);
      return;
    }

    // let user go forward if everything is valid
    // if not, force user to use "next step" button
    form.validateFields(FORM_FIELDS) // FORM_FIELDS is an array that keeps field names
      .then(() => {
        setCurrentStep(step);
      })
      .catch((err) => {
        console.log(err);
        return;
      });
  };

这是我大致整理的方法:

const [form] = Form.useForm();
  const [currentStep, setCurrentStep] = useState(0);

 const initialSteps = {
    form,
    step: (val) => setCurrentStep(val),
    nextStep: () => setCurrentStep((prev) => prev + 1),
  };

const [steps] = useState([
    {
      title: 'Personal',
      content: <PersonalForm {...initialSteps} />,
    },
    {
      title: 'Details',
      content: <DetailsForm {...initialSteps} />,
    },
    {
      title: 'Contact',
      content: <ContactForm {...initialSteps} />,
    }])

return <Steps
          size="small"
          current={currentStep}
          direction="vertical"
          onChange={(current) => handleStepChange(current)}
        >
          {steps.map((item, index) => (
            <Step status={item.status} key={item.title} title={item.title} />
          ))}
        </Steps>

我如何验证确保验证每一个表单字段被卸载,未填写,未触及等?
编辑:我也尝试过分别访问每个步骤的表单对象,希望每个表单对象都包含自己的表单字段,但也没有成功

const handleStepChange = (step) => {
  // let user go back to previous steps
  if (step <= currentStep) {
    setCurrentStep(step);
    return;
  }

  // let user go forward if target step and steps in between are filled in
  const validationFields = [];
  const stepsInbetween = [];
  for (let i = 0; i < step - currentStep; i++) {
    const stepCursor = steps[currentStep + i];
    stepsInbetween.push(stepCursor);
    const stepCursorFields = STEP_FIELDS[stepCursor.content.type.name];
    validationFields.push(...stepCursorFields);
  }
  let isValid = true;
  stepsInbetween.forEach((s) => {
    s.content.props.form
      .validateFields()
      .then(() => {})
      .catch(() => (isValid = false));
  });
  if (isValid) setCurrentStep(step);
};
qkf9rpyu

qkf9rpyu1#

你应该使用Form元素,而不是把每个字段放在一起。Ant Design的表单已经有了内置的数据收集、验证,并对每个字段执行验证,不管它们是否被触摸。
这是一个实现Form的框架表单。你需要用Form.Item Package 每个字段,然后在Form.Item中传入一个对象,作为rules,其中required=True是条目之一。听起来比实际情况更复杂,所以这里有一个片段:

<Form {...layout} form={form} name="control-hooks" onFinish={onFinish}>
      <Form.Item
        name="note"
        label="Note"
        rules={[
          {
            required: true,
          },
        ]}
      >
        <Input />
      </Form.Item>
      <Form.Item
        name="gender"
        label="Gender"
        rules={[
          {
            required: true,
          },
        ]}
      >
        <Select
          placeholder="Select a option and change input text above"
          onChange={onGenderChange}
          allowClear
        >
          <Option value="male">male</Option>
          <Option value="female">female</Option>
          <Option value="other">other</Option>
        </Select>
      </Form.Item>

将检查和验证由<Form.Item /> Package 的任何字段,其中required: true在其规则中。您还可以根据每个字段的要求使用设置rules使其更加复杂。示例:

<Form.Item name={['user', 'age']} label="Age" rules={[{ type: 'number', min: 0, max: 99 }]}>
   <InputNumber />
</Form.Item>

<Form.Item name={['user', 'email']} label="Email" rules={[{ type: 'email' }]}>
  <Input />
</Form.Item>

从它的documentation
表单将自动收集和验证表单数据。
因此,只需依靠Form组件根据您在每个Form.Item上指定的规则来处理验证,就可以保存大量的自定义代码。

编辑1

根据评论中的其他信息,由于您提到您已经使用了<Form.Item>,这将有助于在用户通过useEffect()钩子导航到其他页面时强制运行验证。如果currentStep被更新(通过setCurrentStep),则在useEffect()主体中运行代码。

const MultiStepForm = () => {
  const [form] = Form.useForm();
  const [currentStep, setCurrentStep] = useState(1);

  useEffect(() => {
    form.validateFields()
      .then(() => {
        // do whatever you need to
      })
      .catch((err) => {
        console.log(err);
      });
  }, [currentStep]);

  const handleStepChange = (step) => {
    // other operations here
    ...
    setCurrentStep(step);
  };
  ...
}
n9vozmp4

n9vozmp42#

这是因为form对象的validateFields方法只验证当前在DOM中呈现的字段。
以下是一个解决方法:
渲染所有对象,但仅使选定对象可见

const formList: any = [
    <FirstPartOfForm isVisible={currentStep === 0} />,
    <SecondPartOfForm isVisible={currentStep === 1} />,
    <ThirdPartOfForm isVisible={currentStep === 2} />
  ];

  const formElements = formList.map((Component: any, index: number) => {
    return <div key={index}>{Component}</div>;
  });

  ...

  <Form ...props>
    <div>{formElements}</div>
  </Form>

然后在构成窗体的组件中:

<div style={{ visibility: isVisible ? "visible" : "hidden", height: isVisible ? "auto" : 0 }}>
  <Form.Item>...</Form.Item>
  <Form.Item>...</Form.Item>
</div>

相关问题