【antd】如何借助antd Form实现自定义表单校验(模拟antd校验效果)

开始前先看下这篇:https://editor.csdn.net/md/?articleId=134720990

当我们做自定义组件时,偶尔会有迫不得已需要自己修改antd默认校验文案的位置的时候,可以看这篇文章。

假设,我们有一个通用渲染组件,可以渲染多种表单项,但每次仅渲染一个,取名为RenderOne
如下所示,我们的Form.Item正是在RenderOne中,他还有一个父级组件CompRender使用Form标签,指定了form=form, 定义是const form= useForm(),并return了RenderOne

// CompRender.js
{renderFromList.map((item) => {
            return (
              <RenderOne
                key={item.key || item.formId}
                tag={item.tag}
                config={item}
                form={trulyForm}
                formValueMap={trulyFormValueMap}
              />
            );
          })}
// RenderOne.js
const CommonRender = commonComponent[tag]; // 这边的这个commonComponent就是引入了一个暴露所有类型组件的js对象,根据tag标签进行指定当前需要渲染的组件,以下的...dynamicProps,otherProps等等属性都是将上层组件传递进来的进行解构传递

return (
      <>
        <Form.Item
          {...dynamicProps}
          label={labelProps}
          extra={isDisabled(config) ? null : extra}
          colon={false}
          rules={isDisabled(config) ? null : setRules(config, trulyForm)}
          initialValue={formItemInitialValue}
          style={style}
          validateFirst={config?.options?.validateFirst || false}
          className={classNames(className)}
        >
          {!children ? (
            <CommonRender
              config={{ ...config, titleSubfix }} // ...config接收来自父级组件的各个Props
              formValueMap={trulyFormValueMap}
              containerId={containerId}
              form={form}
              getPopupContainer={(trigger) => trigger.parentNode}
              {...otherProps}
              {...initexpandedKeyProps}
              direction={direction}
            />
          ) : (
            children
          )}
        </Form.Item>
      </>

在以上基础上,再次假设我们有一个地址组件(就是我们上面提到的,被暴露出来的组件之一,取名为area.js),需要选择省市区并填写详细地址。并且我们是在一个Form.Item中渲染的address Cascader组件,并且在address下面再定义一个Form.Item并写入详细地址输入框组件addrDetail LimitInput,像这样:

// area.js
<Cascader
                  fieldNames={{
                    id: 'code',
                    label: 'name',
                    value: 'name',
                  }}
                  showSearch={initialShowSearch}
                  changeOnSelect={changeOnSelect}
                  options={area}
                  value={initialValue}
                  valueType={valTyp}
                  onChange={handleChanged}
                  {...optionsProps}
                  {...otherprops}
                />

                <div style={{ marginTop: mgTp }}>
                  <LimitInput
                    area
                    formItemOpts={{
                      formItemName: getFormId(),
                      rules: [
                        {
                          required:
                            config?.rules[0]?.required,
                          message: optionsProps.placeholder2,
                        },
                      ],
                    }}
                    value={detailAddr}
                    rows={3}
                    // disabled
                    placeholder={optionsProps.placeholder2}
                    style={{ wordBreak: 'break-all' }}
                    maxLength={maxLength}
                    detailIsNeed={detailIsNeed}
                  />
                </div>
// LimitInput.js
<Form.Item name={formItemOpts.formItemName} noStyle rules={formItemOpts.rules}>
        <Input.TextArea ref={cRef} {...rest} style={{ paddingBottom: 24, ...rest?.style }} />
      </Form.Item>

像上面这么写,默认情况下地址详细地址的表单检验文案会放置在最底下(地址在最上方,详细地址在下方,校验文案在详细地址的底下),这个位置的原因是因为他们是父子级的formItem嵌套,现在UI要求必须让地址的表单校验文案放在地址的下面,详细地址上面。这时候就需要自己校验规则并添加自定义样式进行处理了。效果图参考如下:
在这里插入图片描述

以上在Form.Item标签属性中rules={isDisabled(config) ? null : setRules(config, trulyForm)},我们注意到有一个用来自定义校验规则的setRules方法:

/** 校验规则处理 */
const setRules = (config, form) => {
  const { tag, rules, cmpType, pickerType, formId } = config;

  // 给基础组件换msg
  const newRules = rules?.map((item) => {
    const attr = { ...item };
    let message = baseMessageObj[tag] || '默认校验提示语';
    return {
      ...attr,
      message,
    };
  });

  if (tag === 'Area' && config.areaFormat === 1) {
    const isRequired = rules?.[0]?.required;
    const _newRules = newRules.map((ruleItme) => {
      // 此时当前的ruleItme的数据为{ required: true, message: '请选择地区/国家' }
      ruleItme.message = ''; // 这个是重点,目的是覆盖掉antd自带的表单校验信息,如果不覆盖掉,我们就无法将自定义的校验文案设置到默认的校验文案的位置,因为会出现重复提示,被覆盖的message置空后,就不会再显示出校验文案,也不会占用一行展示空字符串校验。
      return ruleItme;
    });
    return [
      ..._newRules, // 这个配置必须继承,数据为{ required: true, message: '' }
      {
        validator: (rule, rulesValue, callback) => {
          if (isRequired) {
            if (!rulesValue?.[0]) {
              callback(
                <div className={classNames('commonAreaRuleTextPosition')}> // commonAreaRuleTextPosition这个就是我们自定义的校验文案样式
                  请选择国家/地区
                </div>,
              );
            }
          }
          callback();
        },
      },
    ];
  }

  return newRules;
};

补充一个,如何设置antd表单不触发校验时的样式,设置这个的目的是为了设置类似嵌套表单项,其中有一个是选填的,如果是自定义的组件就会被一起触发,我们需要自主控制其选填的边框、hover、focus、focused、focus-within样式。
效果如下:
(1)默认效果
在这里插入图片描述
(2)鼠标hover覆盖富文本框但没有点击选中
在这里插入图片描述
(3)鼠标点击选中富文本框在这里插入图片描述

// render function
const renderTextArea = formItemOpts ? (
      <Form.Item name={formItemOpts.formItemName} noStyle rules={formItemOpts.rules}>
        <Input.TextArea ref={cRef} {...rest} style={{ paddingBottom: 24, ...rest?.style }} />
      </Form.Item>
    ) : (
      <Input.TextArea ref={cRef} {...rest} style={{ paddingBottom: 24, ...rest?.style }} />
    );
return <div className={!rest.detailIsNeed && !rest.readOnly ? `${styles.detailAddr}` : ``}>{renderTextArea}</div>

// style.less
.detailAddr {
      :global {
        .ant-input {
          border-color: #e8eaef !important;
        }
        .ant-input:hover,
        .ant-input:focus,
        .ant-input-focused {
          border-color: #82b4ff !important;
          outline: none !important;
          box-shadow: 0 0 6px rgba(0, 0, 0, 0.1) !important;
        }
        .ant-input:focus,
        .ant-input-focused {
          box-shadow: 0 0 4px #82b4ff !important;
        }
        .ant-input-affix-wrapper:focus-within {
          border-color: #82b4ff !important;
          box-shadow: 0 0 4px #82b4ff !important;
        }
      }
    }
  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
在使用 Ant Design Vue 的 `a-form` 表单时,自定义校验器可以使用 `validator` 属性来指定。默认情况下,校验器会对表单项进行非空判断。如果你想要自定义校验器但不想进行非空判断,可以在校验器函数中手动判断。 例如,你可以定义一个自定义校验器函数来检查输入值是否为正整数: ```javascript const validatePositiveInteger = (rule, value, callback) => { if (value && !/^[1-9]\d*$/.test(value)) { callback(new Error('请输入正整数')) } else { callback() } } ``` 在这个例子中,我们首先判断了输入值是否存在,如果存在,再使用正则表达式来判断输入值是否为正整数,如果不是,则调用 `callback` 函数并传递错误信息。如果输入值是正整数,则调用 `callback` 函数并传递一个空参数,表示校验通过。 然后,你可以在表单项中使用这个自定义校验器,并在 `rules` 属性中指定它: ```html <a-form-item label="正整数"> <a-input v-model:value="value" /> </a-form-item> ``` ```javascript { data() { return { value: '' } }, setup() { const { validatePositiveInteger } = yourValidateFunctions const rules = { value: [ { required: true, message: '请输入值' }, { validator: validatePositiveInteger, message: '请输入正整数' } ] } return { rules } } } ``` 在这个例子中,我们在 `rules` 属性中定义了一个校验规则,其中包括了一个非空校验规则和一个自定义校验规则。当表单项的值发生变化时,Ant Design Vue 会自动触发这些校验规则,然后根据校验结果来显示错误信息或者提交表单

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hzxOnlineOk

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值