1
votes

I have a form that has an collection of checkboxes where the name is: choices[]. I have a PHP backend and the name of the checkboxes cant change. I'm trying to build the Yup schema to create a validation rule where one of the choices has to be picked.

My current solution is to have an onchange the updates a hidden field with a value. This value is what is validated against. But that cant be right.

Any idea? I've tried, but this isnt working. I cant seem to find a good example of this on the web so i've decided to ask here.

choices[]: Yup.boolean().required('error we need to pick one')

EDIT to add in some code:

import React, { ChangeEvent, useState, useEffect } from 'react'

import Checkbox from ...
import CheckboxProps from ...
import CheckboxGroup from ...
import Label from ...

export interface CheckboxGroupChoicesProps {
  setFieldValue?: any
}

/**
 *
 * @param props
 */
const CheckboxGroupChoices: React.FunctionComponent<
  CheckboxGroupChoicesProps
> = props => {
  /**
   * State management for the checked counter, we use this in a hidden field for validation purposes
   */
  const [checkedCounter, setCheckedCounter] = useState(0)
  const [initialLoad, setInitialLoad] = useState(1)
  const hiddenFieldName = 'cbxSFVal'

  /**
   * Enable validation on this checkbox field
   *
   * This is a required field, as such we need to ensure that at least one of the
   * checkboxes has been selected. By incrementing the <hidden> field we ensure
   * that we have an idea on how many of the checkboxes have been selected. We
   * then use this within the validation.tsx to do some stuff.
   *
   * @param e: ChangeEvent
   */
  function validateCheckboxRequired(e: ChangeEvent) {
    if ((e.currentTarget as HTMLInputElement).checked) {
      setCheckedCounter(checkedCounter + 1)
    } else {
      setCheckedCounter(checkedCounter - 1)
    }
  }

  /**
   * Handle the state change for checkedCounter.
   * setFieldValue is from formik and we can use it to update the hidden field
   * that we use for validation.
   */
  useEffect(() => {
    if (initialLoad != 1) {
      props.setFieldValue(hiddenFieldName, checkedCounter)
    } else {
      setInitialLoad(0)
    }
  }, [checkedCounter])

  /**
   * Create an array of checkboxes that will be sent to the Checkboxgroup molecule.
   *
   * @return Array<ChecboxProps>
   */
  const getCheckboxes: any = () => {
    const checkboxes: Array<React.ReactElement<CheckboxProps>> = []
    const options = ['Car', 'Van', 'Bike']

    options.forEach((option, key) => {
      checkboxes.push(
        <Checkbox
          label={option}
          value={option}
          disabled={false}
          name={'choices[]'}
          key={`choices-${key}`}
          handleOnClick={(e: ChangeEvent) => validateCheckboxRequired(e)}
        />
      )
    })

    return checkboxes
  }

  return (
    <>
      <Label>Pick choices</Label>
      <CheckboxGroup columns={3}>{getCheckboxes()}</CheckboxGroup>
      <input type={'hidden'} name={hiddenFieldName} />
    </>
  )
}

export default CheckboxGroupChoices

My component essentiall renders the following html:

<div>
<checkbox name='choices[]' value='car'> Car<br />
<checkbox name='choices[]' value='van'> Car<br />
<checkbox name='choices[]' value='bike'> Car<br />
</div>

I use formik for my validation using a validation schema like so:

<Formik
    enableReinitialize
    initialValues={postingForm.initialValues}
    validationSchema={postingForm.validationSchema}
>
{form => (
  <Form> ....</Form>
)}
</Formik>

On submit i want to validate that at least one of my choices has been checked. My current component will +1 and -1 the value of a hidden field based on checked status for a checkbox. This is validated using:

cbxSFVal: Yup.number().min(1, required_message),

But i'm sure there must be an easier way to validate the checkboxes.

1

1 Answers

1
votes

You need to set the property name with quotes.

const schema = Yup.object().shape({
  "choices[]": Yup.boolean().required('error we need to pick one')
});

Also, make sure you are accessing the property the correct way.

// Syntax Error
obj.choices[]

// Correct Way
obj["choices[]"]

Edit:

What you can do is have an array with the state of each checkbox so if you have 3 options, it would be an array of length 3 with false and you update the value to true when clicked.

This way, you can validate that one of the checkboxes are true with

"choices[]": Yup.array().oneOf(['true']).required('error we need to pick one')