Joi:无法更改可选数组的错误消息,如果有项目,它们不能是空字符串

问题描述 投票:0回答:1

我有一个需要描述的项目表格,如果他们想添加详细信息,则需要填写这些文本区域。但如果没有添加任何细节也没关系。因此,如果他们想添加详细信息,则必须填充该文本区域。但允许空数组。

我无法覆盖缺少详细信息的默认错误,默认错误是

"details[0]" must not be a sparse array

A form that shows a blank textarea underneath the text 'Write a description for your project. (Required)' Beneath the textarea is a button to add a detail. The Save button is disabled.

A form with two textareas, one for the description and a new one asking for a detail that is also blank. The Save button is disabled.

The textareas are now outlined in read. The description textarea has the text 'Description required' underneath. The detail textarea has the text 'details[0] must not be a sparse array item'.

架构:

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>
    );
  }
);
reactjs react-hooks joi
1个回答
0
投票

如果您使用 .custom() 进行验证,则需要确保您的模式返回一个值

© www.soinside.com 2019 - 2024. All rights reserved.