Yup中的条件验证

174

我有一个电子邮件字段,只有在选择复选框(布尔值为true)时才会显示。当表单被提交时,我只想要这个字段在复选框被选中时才是必填项(布尔值为true)。

到目前为止,我尝试过以下方法:

const validationSchema = yup.object().shape({
   email: yup
         .string()
         .email()
         .label('Email')
         .when('showEmail', {
             is: true,
             then: yup.string().required('Must enter email address'),
         }),
    })

我尝试了几个其他的变体,但是Formik和Yup会出错:

Uncaught (in promise) TypeError: Cannot read property 'length' of undefined
    at yupToFormErrors (formik.es6.js:6198)
    at formik.es6.js:5933
    at <anonymous>
yupToFormErrors @ formik.es6.js:6198

而且我也从 Yup 得到了验证错误。我做错了什么?


在设置initialValues对象时,同时将enableReinitialize设置为true也非常重要。 - Klem Lloyd Mwenya
11个回答

200

你可能没有为showEmail字段定义验证规则。

我已经在CodeSandbox中测试过了,只要我添加:

showEmail: yup.boolean()

表单已正确启动验证,未抛出任何错误。

这是链接:https://codesandbox.io/s/74z4px0k8q

以后使用的正确验证模式如下:

validationSchema={yup.object().shape({
    showEmail: yup.boolean(),
    email: yup
      .string()
      .email()
      .when("showEmail", {
        is: true,
        then: yup.string().required("Must enter email address")
      })
  })
}

4
如果showEmail有三个可能的值,而不是一个布尔值,比如是一个字符串类型(string()),那么该怎么使用呢? - DB1500
2
你将使用一个函数向is key添加一个特定的测试: is: (emailValue) => emailValue === "你想匹配的某个测试字符串" - Devin Clark
1
谢谢你的回答,我真的不知道你是怎么想出这个解决方案的,但这反映了许多npm包存在的一个主要问题,即90%的库文档应该从Git问题或SO答案中提取。 - Mohsen Kamrani
5
更新后的 CodeSandbox 地址:https://codesandbox.io/s/formik-conditional-val-1ldmx。该代码示例用 Formik 实现条件验证。请注意,翻译仅限于上述内容,不包括任何解释或其他信息。 - Swar Shah
请记住,有一个与“then”相反的“otherwise”关键字,您可以在其中保留替代验证。 - Sergei Kurochkin
在新的 yup v1 及更高版本中,当进行条件操作时,您必须返回模式,例如 then: (schema) => schema.min(5)... - Georgy Martynovich

82

Formik 的作者在这里...

为了使 Yup.when 正常工作,您需要将 showEmail 添加到 initialValues 中,并将其添加到 Yup 模式中。

一般来说,在使用 validationSchema 时,最好的做法是确保表单的所有字段都具有初始值,以便 Yup 可以立即看到它们。

结果应如下所示:

<Formik 
  initialValues={{ email: '', showEmail: false }}
  validationSchema={Yup.object().shape({
    showEmail: Yup.boolean(),
    email: Yup
      .string()
      .email()
      .when("showEmail", {
        is: true,
        then: Yup.string().required("Must enter email address")
      })
  })
}

/>

11
确保表单所有字段都有初始值的最佳实践是什么...一个常见的例子是数字输入框。假设你希望输入框一开始为空白的,而不是为零或任何其他数字。那么你需要将该字段初始化为未定义。 - J. Munson
当您拥有嵌套表单和对象表示法时,该怎么办?基本上,一个字段的name最终变成例如“tax [0] .country” - visionInc

38

即使是复杂情况,您也可以使用函数。 函数案例有助于进行复杂验证

validationSchema={yup.object().shape({
    showEmail: yup.boolean(),
    email: yup
      .string()
      .email()
      .when("showEmail", (showEmail, schema) => {
        if(showEmail)
          return schema.required("Must enter email address")
        return schema
      })
  })
}

谢谢!对我有效! - Daria Moreno-Gogoleva
这对我来说很有效,没有TypeScript抱怨方法过载。谢谢。 - undefined

25

完全同意@João Cunha的回答。这里只是给单选按钮用例的一个补充。

当我们将单选按钮用作条件时,可以检查字符串的值而不是布尔值。例如:is:'Phone'

const ValidationSchema = Yup.object().shape({
  // This is the radio button.
  preferredContact: Yup.string()
    .required('Preferred contact is required.'),
  // This is the input field.
  contactPhone: Yup.string()
    .when('preferredContact', {
      is: 'Phone',
      then: Yup.string()
        .required('Phone number is required.'),
    }),
  // This is another input field.
  contactEmail: Yup.string()
    .when('preferredContact', {
      is: 'Email',
      then: Yup.string()
        .email('Please use a valid email address.')
        .required('Email address is required.'),
    }),

});

这是使用ReactJS编写的单选按钮,onChange方法是触发条件检查的关键。

<label>
  <input
    name="preferredContact" type="radio" value="Email"
    checked={this.state.preferredContact == 'Email'}
    onChange={() => this.handleRadioButtonChange('Email', setFieldValue)}
  />
  Email
</label>
<label>
  <input
    name="preferredContact" type="radio" value="Phone"
    checked={this.state.preferredContact == 'Phone'}
    onChange={() => this.handleRadioButtonChange('Phone', setFieldValue)}
  />
  Phone
</label>

以下是当单选按钮发生更改时的回调函数。如果我们正在使用 Formik,setFieldValue 是最佳选择。

handleRadioButtonChange(value, setFieldValue) {
  this.setState({'preferredContact': value});
  setFieldValue('preferredContact', value);
}

你节省了很多时间。 - Gimnath

19
email: Yup.string()
    .when(['showEmail', 'anotherField'], {
        is: (showEmail, anotherField) => {
            return (showEmail && anotherField);
        },
        then: Yup.string().required('Must enter email address')
    }),

4

注意使用 Yup v1 及以上版本的人。在我的情况下是 v1.2。 根据官方文档,您必须在条件中执行(schema) => ...

官方文档:
对于具有动态组件(引用、惰性或条件)的模式,describe 需要更多上下文才能准确返回模式描述。在这些情况下提供选项。

import { ref, object, string, boolean } from 'yup';

let schema = object({
  isBig: boolean(),
  count: number().when('isBig', {
    is: true,
    then: (schema) => schema.min(5),
    otherwise: (schema) => schema.min(0),
  }),
});

schema.describe({ value: { isBig: true } });

3

这对我来说非常有效:

   Yup.object().shape({
    voyageStartDate:Yup.date(),
    voyageEndDate:Yup.date()
        .when(
            'voyageStartDate',
            (voyageStartDate, schema) => (moment(voyageStartDate).isValid() ? schema.min(voyageStartDate) : schema),
        ),
})

1
这非常接近我所寻找的内容。如果您想确保END > START,您将如何访问此处.when中的end值?我只想比较是否设置了start - Neil Gaetano Lindberg

1

我使用yup和vee-validate

vee-validate

这是项目中的示例代码

const schema = yup.object({
    first_name: yup.string().required().max(45).label('Name'),
    last_name: yup.string().required().max(45).label('Last name'),
    email: yup.string().email().required().max(255).label('Email'),
    self_user: yup.boolean(),
    company_id: yup.number()
        .when('self_user', {
            is: false,
            then: yup.number().required()
        })
})
const { validate, resetForm } = useForm({
    validationSchema: schema,
    initialValues: {
        self_user: true
    }
})

const {
    value: self_user
} = useField('self_user')
const handleSelfUserChange = () => {
    self_user.value = !self_user.value
}

1

不使用函数符号检查特定值:

如果选择choice的值为date,则输入字段date是必需的:

availableDate: yup.string().when('choice', {
    is: (v) => v === 'date',
    then: (schema) => schema.required('date is required')
})

1
使用TypeScript的方法如下
reasonContractNotDone: yup.string().when('isContractDone', {
  is: false,
  then(schema) {
    return schema.required('Must enter email address');
  },
}),

不要像下面这样!
reasonContractNotDone: yup.string().when('isContractDone', {
  is: false,
  then: yup.string().required("Must enter email address")
}),

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接