53

I have a data structure like this:

{
  "subject": "Ah yeah",
  "description": "Jeg siger...",
  "daysOfWeek": [
    {
      "dayOfWeek": "MONDAY",
      "checked": false
    },
    {
      "dayOfWeek": "TUESDAY",
      "checked": false
    },
    {
      "dayOfWeek": "WEDNESDAY",
      "checked": true
    },
    {
      "dayOfWeek": "THURSDAY",
      "checked": false
    },
    {
      "dayOfWeek": "FRIDAY",
      "checked": false
    },
    {
      "dayOfWeek": "SATURDAY",
      "checked": true
    },
    {
      "dayOfWeek": "SUNDAY",
      "checked": true
    }
  ],
  "uuid": "da8f56a2-625f-400d-800d-c975bead0cff",
  "taskSchedules": [],
  "isInitial": false,
  "hasChanged": false
}

In daysOfWeek I want to ensure that at least one of the items has checked: true.

This is my validation schema so far (but not working):

const taskValidationSchema = Yup.object().shape({
  subject: Yup.string().required('Required'),
  description: Yup.string(),
  daysOfWeek: Yup.array()
    .of(
      Yup.object().shape({
        dayOfWeek: Yup.string(),
        checked: Yup.boolean(),
      })
    )
    .required('Required'),
  taskSchedules: Yup.array(),
})

Is it possible to validate the values of daysOfWeek ensuring that at least one of them has checked: true?

olefrank
  • 6,452
  • 14
  • 65
  • 90

6 Answers6

57

I solved it using compact() (filtering out falsely values) together with setTimeout after the FieldArray modifier function:

const validationSchema = Yup.object().shape({
  subject: Yup.string().required(i18n.t('required-field')),
  description: Yup.string(),
  daysOfWeek: Yup.array()
    .of(
      Yup.object().shape({
        dayOfWeek: Yup.string(),
        checked: Yup.boolean(),
      })
    )
    .compact((v) => !v.checked)
    .required(i18n.t('required-field')),
  taskSchedules: Yup.array(),
});

And in form:

<Checkbox
  value={day.dayOfWeek}
  checked={day.checked}
  onChange={(e) => {
    replace(idx, { ...day, checked: !day.checked });
    setTimeout(() => {
      validateForm();
    });
  }}
/>;
Mario Petrovic
  • 7,500
  • 14
  • 42
  • 62
olefrank
  • 6,452
  • 14
  • 65
  • 90
  • The `setTimeout` thing just made my day :-) There was a really strange behaviour when using `arrayHelper`. Thanks a lot. – Klaus Jan 02 '20 at 10:11
  • 1
    How can I reference to (e.g.) `description` from a `when()` condition in (e.g.) `dayOfWeek`? Basically... `dayOfWeek: Yup.string().when("description", {is: ..., then: ...}` but it doesn't work from inside the array. – KaMZaTa Feb 25 '22 at 01:56
  • If you use `compact()` the form data is also filtered. that means in the form data after validation the array will only contain only truthy values. the solution is to use `test()` for custom validation. `test('atLeastOne', 'error message', days => days.some(day => day.checked))` – SajithK Dec 22 '22 at 03:18
13

Base on @olefrank's answer. This code work with me.

const validationSchema = Yup.object().shape({
  subject: Yup.string().required(i18n.t('required-field')),
  description: Yup.string(),
  daysOfWeek: Yup.array()
    .of(
      Yup.object().shape({
        dayOfWeek: Yup.string(),
        checked: Yup.boolean(),
      })
    )
    .compact((v) => !v.checked)
    .min(1, i18n.t('required-field')), // <– `.min(1)` instead of `.required()`
  taskSchedules: Yup.array(),
});
fredrivett
  • 5,419
  • 3
  • 35
  • 48
Duy Nguyen
  • 161
  • 1
  • 3
4

I have done this type of validation in my Node.js(Express.js) project. You can try validation in this way.

const validationSchema = yup.object({
  subject: yup.string().required(),
  description: yup.string().required(), 
  daysOfWeek: yup.array(
    yup.object({
      dayOfWeek: yup.string().required(),
      checked: yup.boolean().required()
    })
  )
})
MD. SHIFULLAH
  • 913
  • 10
  • 16
3

If you trying in latest version it should be used like this Yup.array().min(1, "At least one option is required").required()

Ravindra
  • 91
  • 4
2

In my case I have used formik with yup for this,

I want to select only one value in an array if not selected I need to display the error

array =  [{"label": "Option 1", "selected": false, "value": "option-1"}, {"label": "Option 2", "selected": false, "value": "option-2"}, {"label": "Option 3", "selected": false, "value": "option-3"}]

Yup.mixed().test({
 message: 'Required',test: 
val => val.filter(i => i.selected === true).length === 1})

it worked for me

Yup.mixed().test({
 message: 'Required',test: 
val => val.filter(i => i.selected === true).length !== 0})
vimuth
  • 5,064
  • 33
  • 79
  • 116
  • Is this describing an problem/error your encounter and want help with? Or is this saying thanks for a proposed solution in an existing answer here? Or should you [edit] this according to [answer] and https://stackoverflow.com/editing-help in order to make more obvious that you are trying to contribute a solution? – Yunnosch Aug 10 '22 at 10:31
0

I did a similar validation like this. You can try this approach

const schema = yup.object().shape({
    subject: yup.string().required(),
    description: yup.string()
    daysOfWeek: yup.array().of(
      yup.lazy(value => {
        const { checked } = value // Get the value of checked field

        return checked
          ? yup.object().shape({
              dayOfWeek: yup.string().required(), // validate only if checked is true
              checked: yup.boolean()
            }) 
           : yup.object().shape({
              dayOfWeek: yup.string(),
              checked: yup.boolean()
            })
      })
    ),
    taskSchedules: yup.array()
})
Haq
  • 35
  • 7