Joi:无法更改可选数组的错误消息,如果有项目,它们不能是空字符串
我有一个需要描述的项目表格,如果他们想添加详细信息,则需要填写这些文本区域。但如果没有添加任何细节也没关系。因此,如果他们想添加详细信息,则必须填充该文本区域。但允许使用空数组。
我在覆盖缺少详细信息的默认错误时遇到问题,默认为“details[0]”不能是稀疏数组
。
架构:
const descriptionDetailSchema = Joi.object({
description: Joi.string().required().messages({
'string.base': 'Description is required',
'string.empty': 'Description is required'
}),
details: Joi.array().items(
Joi.string().messages({
'string.empty': 'Detail is required'
})
)
});
const DescriptionAndDetailForm = forwardRef(
({ activityIndex, item, index, saveDescription, setFormValid }, ref) => {
DescriptionAndDetailForm.displayName = 'DescriptionAndDetailForm';
const {
handleSubmit,
control,
formState: { error, errors, isValid, isValidating },
getValues
} = useForm({
defaultValues: {
description: item.description,
details: item.details
},
mode: 'onBlur',
reValidateMode: 'onBlur',
resolver: joiResolver(descriptionDetailSchema)
});
useEffect(() => {
console.log('isValid changed');
console.log({ errors, isValid, isValidating });
setFormValid(isValid);
}, [isValid]);
useEffect(() => {
console.log('errors changed');
console.log({ errors, isValid, isValidating });
}, [errors]);
useEffect(() => {
console.log('isValidating changed');
const { error, value } = descriptionDetailSchema.validate(getValues());
console.log({ error, value, errors, isValid, isValidating });
}, [isValidating, errors]);
const initialState = item;
function reducer(state, action) {
switch (action.type) {
case 'updateField':
return {
...state,
[action.field]: action.value
};
case 'addDetail': {
const newDetail = newDescriptionDetail();
return {
...state,
details: [...state.details, newDetail]
};
}
case 'removeDetail': {
const detailsCopy = [...state.details];
detailsCopy.splice(action.index, 1);
return {
...state,
details: detailsCopy
};
}
case 'updateDetails': {
const detailsCopy = [...state.details];
detailsCopy[action.detailIndex].detail = action.value;
return {
...state,
details: detailsCopy
};
}
default:
throw new Error(
'Unrecognized action type provided to DescriptionAndDetailForm reducer'
);
}
}
const [state, dispatch] = useReducer(reducer, initialState);
const handleDescriptionChange = e => {
dispatch({
type: 'updateField',
field: 'description',
value: e.target.value
});
};
const onSubmit = e => {
e.preventDefault();
saveDescription(activityIndex, index, state);
handleSubmit(e);
};
const handleAddDetail = () => {
dispatch({ type: 'addDetail' });
};
const handleDeleteDetail = (descriptionIndex, detailIndex) => {
dispatch({ type: 'removeDetail', index: detailIndex });
};
const handleDetailChange = (e, i) => {
dispatch({
type: 'updateDetails',
detailIndex: i,
value: e.target.value
});
};
return (
<form
index={index}
key={`activity${activityIndex}-index${index}-form`}
onSubmit={onSubmit}
>
<Controller
key={`activity${activityIndex}-index${index}`}
name="description"
control={control}
render={({ field: { onChange, ...props } }) => (
<TextField
{...props}
label="Description"
multiline
rows="4"
onChange={e => {
handleDescriptionChange(e);
onChange(e);
}}
errorMessage={errors?.description?.message}
errorPlacement="bottom"
/>
)}
/>
{state.details.map(({ key, detail }, i) => (
<Review
key={key}
onDeleteClick={ () => handleDeleteDetail(index, i) }
onDeleteLabel="Remove"
skipConfirmation
ariaLabel={`${i + 1}. ${detail}`}
objType="Detail"
>
<div>
<Controller
name={`details.${i}`}
control={control}
render={({ field: { onChange, ...props } }) => (
<TextField
{...props}
id={`${activityIndex}-detail${i}`}
name={`details.${i}`}
label="Detail"
value={detail}
multiline
rows="4"
onChange={e => {
handleDetailChange(e, i);
onChange(e);
}}
errorMessage={errors?.details && errors?.details[i]?.message}
errorPlacement="bottom"
/>
)}
/>
</div>
</Review>
))}
<div>
<Button
key={`activity${activityIndex}-index${index}-add-metric`}
onClick={handleAddDetail}
>
<Icon icon={faPlusCircle} />
Add Detail
</Button>
</div>
<input
type="submit"
ref={ref}
hidden
/>
</form>
);
}
);
I have a form for a project where the description is required, and if they want to add details, those textareas need to be filled out. But it's okay if there are no details added. So if they want to add a detail, that text area has to be filled. But an empty array is allowed.
I am having trouble overwriting the default error for the missing details, the default being "details[0]" must not be a sparse array
.
Schema:
const descriptionDetailSchema = Joi.object({
description: Joi.string().required().messages({
'string.base': 'Description is required',
'string.empty': 'Description is required'
}),
details: Joi.array().items(
Joi.string().messages({
'string.empty': 'Detail is required'
})
)
});
const DescriptionAndDetailForm = forwardRef(
({ activityIndex, item, index, saveDescription, setFormValid }, ref) => {
DescriptionAndDetailForm.displayName = 'DescriptionAndDetailForm';
const {
handleSubmit,
control,
formState: { error, errors, isValid, isValidating },
getValues
} = useForm({
defaultValues: {
description: item.description,
details: item.details
},
mode: 'onBlur',
reValidateMode: 'onBlur',
resolver: joiResolver(descriptionDetailSchema)
});
useEffect(() => {
console.log('isValid changed');
console.log({ errors, isValid, isValidating });
setFormValid(isValid);
}, [isValid]);
useEffect(() => {
console.log('errors changed');
console.log({ errors, isValid, isValidating });
}, [errors]);
useEffect(() => {
console.log('isValidating changed');
const { error, value } = descriptionDetailSchema.validate(getValues());
console.log({ error, value, errors, isValid, isValidating });
}, [isValidating, errors]);
const initialState = item;
function reducer(state, action) {
switch (action.type) {
case 'updateField':
return {
...state,
[action.field]: action.value
};
case 'addDetail': {
const newDetail = newDescriptionDetail();
return {
...state,
details: [...state.details, newDetail]
};
}
case 'removeDetail': {
const detailsCopy = [...state.details];
detailsCopy.splice(action.index, 1);
return {
...state,
details: detailsCopy
};
}
case 'updateDetails': {
const detailsCopy = [...state.details];
detailsCopy[action.detailIndex].detail = action.value;
return {
...state,
details: detailsCopy
};
}
default:
throw new Error(
'Unrecognized action type provided to DescriptionAndDetailForm reducer'
);
}
}
const [state, dispatch] = useReducer(reducer, initialState);
const handleDescriptionChange = e => {
dispatch({
type: 'updateField',
field: 'description',
value: e.target.value
});
};
const onSubmit = e => {
e.preventDefault();
saveDescription(activityIndex, index, state);
handleSubmit(e);
};
const handleAddDetail = () => {
dispatch({ type: 'addDetail' });
};
const handleDeleteDetail = (descriptionIndex, detailIndex) => {
dispatch({ type: 'removeDetail', index: detailIndex });
};
const handleDetailChange = (e, i) => {
dispatch({
type: 'updateDetails',
detailIndex: i,
value: e.target.value
});
};
return (
<form
index={index}
key={`activity${activityIndex}-index${index}-form`}
onSubmit={onSubmit}
>
<Controller
key={`activity${activityIndex}-index${index}`}
name="description"
control={control}
render={({ field: { onChange, ...props } }) => (
<TextField
{...props}
label="Description"
multiline
rows="4"
onChange={e => {
handleDescriptionChange(e);
onChange(e);
}}
errorMessage={errors?.description?.message}
errorPlacement="bottom"
/>
)}
/>
{state.details.map(({ key, detail }, i) => (
<Review
key={key}
onDeleteClick={ () => handleDeleteDetail(index, i) }
onDeleteLabel="Remove"
skipConfirmation
ariaLabel={`${i + 1}. ${detail}`}
objType="Detail"
>
<div>
<Controller
name={`details.${i}`}
control={control}
render={({ field: { onChange, ...props } }) => (
<TextField
{...props}
id={`${activityIndex}-detail${i}`}
name={`details.${i}`}
label="Detail"
value={detail}
multiline
rows="4"
onChange={e => {
handleDetailChange(e, i);
onChange(e);
}}
errorMessage={errors?.details && errors?.details[i]?.message}
errorPlacement="bottom"
/>
)}
/>
</div>
</Review>
))}
<div>
<Button
key={`activity${activityIndex}-index${index}-add-metric`}
onClick={handleAddDetail}
>
<Icon icon={faPlusCircle} />
Add Detail
</Button>
</div>
<input
type="submit"
ref={ref}
hidden
/>
</form>
);
}
);
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
如果您使用 .custom() 进行验证,您需要确保您的模式返回一个值
You need to make sure your schema is returning a value if you're using .custom() for validation