How to validate with Yup that in array of objects at least one of object keys has true value?

Viewed 11018

I have a form with dynamical fields (answers). I'm using Formik and Yup for validations. One answer is object {text: string, is_correct: boolean}. I need to make validation schema that at least one answer should have is_correct: true How to make validation in this case?

function answersSchema (rules) {
    const {
        QUIZ_ANSWER_MAX_LENGTH,
        QUIZ_ANSWER_MIN_LENGTH,
    } = rules;
    return Yup.object().shape({
        text: Yup.string()
            .min(QUIZ_ANSWER_MIN_LENGTH, 'Too Short!')
            .max(QUIZ_ANSWER_MAX_LENGTH, 'Too Long!').required('Text of answer is required'),
        is_correct: Yup.boolean().required()
    })
}

export function setQuizSchema(rules) {
    const {
        QUIZ_QUESTION_MAX_LENGTH,
        QUIZ_QUESTION_MIN_LENGTH,
        QUIZ_DESCRIPTION_MAX_LENGTH,
        QUIZ_DESCRIPTION_MIN_LENGTH,
        QUIZ_MAX_COUNT_OF_ANSWERS,
        QUIZ_MIN_COUNT_OF_ANSWERS,
    } = rules;
    return Yup.object().shape({
        question: Yup.string()
            .min(QUIZ_QUESTION_MIN_LENGTH, 'Too Short!')
            .max(QUIZ_QUESTION_MAX_LENGTH, 'Too Long!')
            .required('Required'),
        description: Yup.string()
            .min(QUIZ_DESCRIPTION_MAX_LENGTH, 'Too Short!')
            .max(QUIZ_DESCRIPTION_MIN_LENGTH, 'Too Long!'),
        answers: Yup.array().of(answersSchema(rules))
            .min(QUIZ_MIN_COUNT_OF_ANSWERS, `The quiz should have a minimum of ${QUIZ_MIN_COUNT_OF_ANSWERS} answers`)
            .max(QUIZ_MAX_COUNT_OF_ANSWERS, `The quiz should have a maximum of ${QUIZ_MAX_COUNT_OF_ANSWERS} answers`)
    })
}

1 Answers

You could use the test method on yup schema, so your code could be something like this

...
return Yup.object().shape({
        question: Yup.string()
            .min(QUIZ_QUESTION_MIN_LENGTH, 'Too Short!')
            .max(QUIZ_QUESTION_MAX_LENGTH, 'Too Long!')
            .required('Required'),
        description: Yup.string()
            .min(QUIZ_DESCRIPTION_MAX_LENGTH, 'Too Short!')
            .max(QUIZ_DESCRIPTION_MIN_LENGTH, 'Too Long!'),
        answers: Yup.array().of(answersSchema(rules))
            .min(QUIZ_MIN_COUNT_OF_ANSWERS, `The quiz should have a minimum of ${QUIZ_MIN_COUNT_OF_ANSWERS} answers`)
            .max(QUIZ_MAX_COUNT_OF_ANSWERS, `The quiz should have a maximum of ${QUIZ_MAX_COUNT_OF_ANSWERS} answers`)
            .test('test-name', 'custom error message', answers => {
                return answers.some(answer => answer.is_correct)
             })
    })
    

as a side note, it is better to use const for declarations that wont change like the variable declarations in the answersSchema function.

Related