hapijs / joi

The most powerful data validation library for JS
Other
20.96k stars 1.51k forks source link

Require at least 1 field using .or() #2862

Closed lagroms closed 1 year ago

lagroms commented 2 years ago

Support plan

Context

How can we help?

I'm trying to require the presence of at least 1 field between "years" and "months". If "months" has a value, then "years" is optional, and the opposite would be true as well. That's the current code that I have using .or() method, but it doesn't seem to work.

    const schema = Joi.object({
    software: Joi.object({
        value: Joi.string(),
        label: Joi.string(),
    })
        .required()
        .messages({ "any.required": "Software can't be empty", "object.base": "Software can't be empty" })
        .label("Software"),

    years: Joi.object()
        .keys({
            value: Joi.number(),
            label: Joi.number(),
        })
        .optional()
        .allow(null),

    months: Joi.object()
        .keys({
            value: Joi.number(),
            label: Joi.number(),
        })
        .optional()
        .allow(null),

    proficiency: Joi.number()
        .required()
        .messages({ "any.required": "Proficiency is required", "number.base": "Proficiency is required" }),
}).or("years", "months");

const {
    register,
    setValue,
    formState: { errors, isDirty },
    watch,
    handleSubmit,
    control,
} = useForm({
    resolver: joiResolver(schema),
    mode: "onSubmit",
    defaultValues: {
        proficiency: null,
        software: null,
        years: null,
        months: null,
    },
});

I've also tried something like :


    const schema = Joi.object({
    software: Joi.object({
        value: Joi.string(),
        label: Joi.string(),
    })
        .required()
        .messages({ "any.required": "Software can't be empty", "object.base": "Software can't be empty" })
        .label("Software"),

    years: Joi.when("months", {
        is: null,
        then: Joi.object({
            value: Joi.number(),
            label: Joi.number(),
        }).required(),
        otherwise: Joi.allow(null),
    }),

    months: Joi.when("years", {
        is: null,
        then: Joi.object({
            value: Joi.number(),
            label: Joi.number(),
        }).required(),
        otherwise: Joi.allow(null),
    }),

    proficiency: Joi.number()
        .required()
        .messages({ "any.required": "Proficiency is required", "number.base": "Proficiency is required" }),
});

but in that case, i'm getting the following error: item added into group months created a dependencies error

Birkbjo commented 1 year ago

This is likely because you are passing null. By default .or() checks for resolved !== undefined. So either set years: undefined, months: undefined or override isPresent-option in the call to

.or('months', 'years', { isPresent: resolved => resolved != undefined })

See docs: https://joi.dev/api/?v=17.8.3#objectorpeers-options

Marsup commented 1 year ago

That's correct, except isPresent should be a function, so .or('months', 'years', { isPresent: (resolved) => resolved !== undefined && resolved !== null })

Birkbjo commented 1 year ago

Yeah, thats right... Updated comment now, Sorry for the typo.

Marsup commented 1 year ago

Np, thanks for contributing 🙂

lagroms commented 1 year ago

Thanks for the help !