简体   繁体   English

使用Joi和/或Yup进行模式验证,如果存在其他任何属性,是否可以收集所需的属性?

[英]Using Joi and/or Yup for schema validation, is there a way to make a collection of properties required if any of the other properties are present?

Take for example an HTML form that collects the details of a person. 例如,收集个人详细信息的HTML表单。

样本表格

Note: Code snippets are simplified and do not match up with the screenshot exactly. 注意:代码段已简化,并且与屏幕截图不完全匹配。 Also, code snippets are written using Yup, which is a very similar library to Joi that targets the browser rather than NodeJS. 另外,代码片段是使用Yup编写的,Yup是与Joi非常相似的库,其目标是浏览器而不是NodeJS。

In order to submit the form, I want to run validations on the address fields and make them required, but only if the user has partially filled out the address section. 为了提交表单,我想在地址字段上运行验证并使其成为必需,但前提是用户已经部分填写了地址部分。 As a whole, I want to make the address details optional. 总体而言,我想使地址详细信息为可选。

Here is a simplified version of my PersonSchema... 这是我的PersonSchema的简化版本...

import { object, string, number } from 'yup'

const PersonSchema = object().shape({
  name: string().required(),
  age: number()
    .positive()
    .integer()
    .required(),
  address: AddressSchema
})

Defining AddressSchema in this way does not work because the fields are always required... 以这种方式定义AddressSchema不起作用,因为始终需要字段...

const AddressSchema = object().shape({
  street: string()
    .min(2, 'Too Short!')
    .max(50, 'Too Long!')
    .required(),
  city: string()
    .min(2, 'Too Short!')
    .max(50, 'Too Long!')
    .required(),
  state: string()
    .min(2, 'Too Short!')
    .max(50, 'Too Long!')
    .required()
})

And here was my attempt to make address fields dependent on the presence of other address fields, but this does not work because you run into circular dependency issues... 这是我尝试使地址字段依赖于其他地址字段的存在,但这不起作用,因为您遇到了循环依赖问题...

const AddressSchema = object().shape({
  street: string()
    .when(['city', 'state'], {
      is: (city, state) => city || state,
      then: string()
        .min(2, 'Too Short!')
        .max(50, 'Too Long!')
        .required(),
      otherwise: string()
    }),
  city: string()
    .when(['street', 'state'], {
      is: (street, state) => street || state,
      then: string()
        .min(2, 'Too Short!')
        .max(50, 'Too Long!')
        .required(),
      otherwise: string()
    }),
  state: string()
    .when(['street', 'city'], {
      is: (street, city) => street || city,
      then: string()
        .min(2, 'Too Short!')
        .max(50, 'Too Long!')
        .required(),
      otherwise: string()
    })
})

I needed the same optional address check: Rather than checking each field based against every other address field, just set a flag in your form/schema named something like 'addressStarted', then use that as your when trigger. 我需要相同的可选地址检查:与其对照每个其他地址字段检查每个字段,不如在表单/模式中设置一个名为“ addressStarted”的标志,然后将其用作when触发器。 The flag was an explicit choice by the user in my case, but it could just as easily be a hidden value; 在我的情况下,标志是用户的明确选择,但也可以很容易地将其作为隐藏值。 just toggle the value to true in every onChange handler (if you're using something like Formik), or even just an event listener on an element that contains all the address fields. 只需在每个onChange处理程序中将值切换为true(如果您使用的是Formik之类的东西),或者甚至是包含所有地址字段的元素上的事件侦听器。

import { object, string, number } from 'yup'

const AddressSchema = object().shape({
  street: string()
    .min(2, 'Too Short!')
    .max(50, 'Too Long!')
    .required(),
  city: string()
    .min(2, 'Too Short!')
    .max(50, 'Too Long!')
    .required(),
  state: string()
    .min(2, 'Too Short!')
    .max(50, 'Too Long!')
    .required()
})

const PersonSchema = object().shape({
  name: string().required(),
  age: number()
    .positive()
    .integer()
    .required(),
  addressStarted: Yup.boolean(),
  address: object().when('addressStarted', {
    is: true,
    then: AddressSchema,
    otherwise: object().notRequired()
  }),
})

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM