简体   繁体   English

如何验证最后 forms 日期的单个输入文本字段,并在日期无效时显示错误消息

[英]How to validate single input textfields which forms a date in the end and show an error message when the date is not valid

I have a React Material UI component that serves to register a user's 'Date of birth'.我有一个 React Material UI 组件,用于注册用户的“出生日期”。 The component is built with 3 different inputs for the day, month, and year.该组件使用 3 种不同的输入构建,分别代表日、月和年。

The example in the screenshot屏幕截图中的示例

输入示例

What I need to do is to show an error when a user inputs a wrong date for example I could write 31 - Feb - 3000 and when I press the submit tick I should see a red box on both Day and year with a text saying like invalid date / invalid year.我需要做的是在用户输入错误日期时显示错误,例如我可以写31 - Feb - 3000当我按下提交勾选时,我应该在 Day 和 year 看到一个红色框,上面写着类似的文字无效日期/无效年份。

The example of what I want to achieve我想要实现的示例

目标

I have no clue how to do that as first time doing a component like this and need help.我不知道如何做到这一点,因为第一次做这样的组件需要帮助。

I'm sharing the partial component regarding those 3 fields in case I can send more details if need it based on the comment I'll receive.我正在分享关于这 3 个字段的部分组件,以防我可以根据收到的评论发送更多详细信息。

<EditableDetailTemplate
      onStartEditing={() => {
        setEditValue(editValue);
      }}
      onDoneEditing={() => {
        editDateOfBirth(month, year, day);
      }}
      displayComponent={
        <Box sx={{ display: 'flex', alignItems: 'center' }}>
          <DobAndAge dateOfBirth={editValue} />
        </Box>
      }
      editComponent={
        <Stack
          direction="row"
          justifyContent="flex-start"
          alignItems="flex-start"
          spacing={3}
        >
          <TextField
            sx={{ width: 100 }}
            value={day}
            onChange={e => setDay(Number(e.target.value))}
            placeholder={intl.formatMessage({ defaultMessage: 'DD' })}
            label={intl.formatMessage({ defaultMessage: 'Day' })}
            inputProps={{
              inputMode: 'numeric',
              pattern: '[0-9]*',
              min: 1,
              max: maxDay || 31,
            }}
            type="number"
          />
          <FormControl>
            <InputLabel id="month-select">{intl.formatMessage({ defaultMessage: 'Month' })}</InputLabel>
            <Select
              sx={{ width: 100 }}
              labelId="month-select"
              value={month}
              label={intl.formatMessage({ defaultMessage: 'Month' })}
              onChange={e => setMonth(Number(e.target.value))}
            >
              {months.map(m => (
                <MenuItem key={m.monthString} value={m.monthNumber}>
                  {m.monthString}
                </MenuItem>
              ))}
            </Select>
          </FormControl>
          <TextField
            sx={{ width: 100 }}
            value={year}
            onChange={e => setYear(Number(e.target.value))}
            placeholder={intl.formatMessage({ defaultMessage: 'YYYY' })}
            label={intl.formatMessage({ defaultMessage: 'Year' })}
            inputProps={{
              inputMode: 'numeric',
              pattern: '[0-9]*',
              max: new Date().getFullYear(),
            }}
            type="number"
          />
        </Stack>
      }
    />

As per comments adding everything related to the component根据评论添加与组件相关的所有内容

full component全组件

import { format } from '@lib/date-util';
import {
  Box,
  FormControl,
  InputLabel,
  MenuItem,
  Select,
  Stack,
  TextField
} from '@mui/material';
import { useEffect, useState } from 'react';
import { useIntl } from 'react-intl';
import { calculateAge, formatOnlyDateToLocale, monthsList } from '../../utils/date-util';
import { EditableDetailTemplate } from './EditableDetailTemplate';

export const DateOfBirth = ({ dateOfBirth, onEdit }) => {
  const intl = useIntl();
  const [editValue, setEditValue] = useState(dateOfBirth);

  const dob = dateOfBirth ? new Date(dateOfBirth) : null;

  const [month, setMonth] = useState(dob?.getMonth() + 1);
  const [year, setYear] = useState(dob?.getFullYear());
  const [day, setDay] = useState(dob?.getDate());

  const fullDate =
    Boolean(year) && Boolean(month) ? new Date(`${year}-${month}-01`) : null;

  const maxDay = fullDate
    ? new Date(fullDate.getFullYear(), fullDate.getMonth() + 1, 0).getDate()
    : null;

  const editDateOfBirth = () => {
    let newYear;
    let newMonth;
    let newDate;

    const oldYear = dob?.getFullYear() || null;
    const oldMonth = dob?.getMonth() + 1 || null;
    const oldDate = dob?.getDate() || null;

    if (oldMonth !== month) {
      newMonth = month;
    }

    if (oldYear !== year) {
      newYear = year;
    }

    if (oldDate !== day) {
      newDate = day;
    }

    const oldDob = `${oldYear}-${oldMonth}-${oldDate}`;
    const newDob = `${newYear || oldYear}-${newMonth || oldMonth}-${
      newDate || oldDate
    }`;

    const oldDobFormatted = !oldDob.includes('null')
      ? format(new Date(oldDob), 'yyyy-MM-dd')
      : null;
    const newDobFormatted = format(new Date(newDob), 'yyyy-MM-dd');

    onEdit(oldDobFormatted, newDobFormatted);
  };

  useEffect(() => {
    setEditValue(dateOfBirth);
  }, [dateOfBirth]);

  const months = monthsList();

  return (
    <EditableDetailTemplate
      onStartEditing={() => {
        setEditValue(editValue);
      }}
      onDoneEditing={() => {
        editDateOfBirth(month, year, day);
      }}
      displayComponent={
        <Box sx={{ display: 'flex', alignItems: 'center' }}>
          <DobAndAge dateOfBirth={editValue} />
        </Box>
      }
      editComponent={
        <Stack
          direction="row"
          justifyContent="flex-start"
          alignItems="flex-start"
          spacing={3}
        >
          <TextField
            sx={{ width: 100 }}
            value={day}
            onChange={e => setDay(Number(e.target.value))}
            placeholder={intl.formatMessage({ defaultMessage: 'DD' })}
            label={intl.formatMessage({ defaultMessage: 'Day' })}
            inputProps={{
              inputMode: 'numeric',
              pattern: '[0-9]*',
              min: 1,
              max: maxDay || 31,
            }}
            type="number"
          />
          <FormControl>
            <InputLabel id="month-select">{intl.formatMessage({ defaultMessage: 'Month' })}</InputLabel>
            <Select
              sx={{ width: 100 }}
              labelId="month-select"
              value={month}
              label={intl.formatMessage({ defaultMessage: 'Month' })}
              onChange={e => setMonth(Number(e.target.value))}
            >
              {months.map(m => (
                <MenuItem key={m.monthString} value={m.monthNumber}>
                  {m.monthString}
                </MenuItem>
              ))}
            </Select>
          </FormControl>
          <TextField
            sx={{ width: 100 }}
            value={year}
            onChange={e => setYear(Number(e.target.value))}
            placeholder={intl.formatMessage({ defaultMessage: 'YYYY' })}
            label={intl.formatMessage({ defaultMessage: 'Year' })}
            inputProps={{
              inputMode: 'numeric',
              pattern: '[0-9]*',
              max: new Date().getFullYear(),
            }}
            type="number"
          />
        </Stack>
      }
    />
  );
};

const DobAndAge = ({ dateOfBirth }) =>
  dateOfBirth
    ? `${formatOnlyDateToLocale(dateOfBirth)} 
(age: ${calculateAge(dateOfBirth)})`
    : '';

The parent component is only relates to this part父组件只与这部分有关

The onEdit part onEdit 部分

const onEditDateOfBirth = async (oldDob, newDob) => {
    if (oldDob !== newDob) {
      try {
        await updateCandidateDetails({ dateOfBirth: newDob });
      } catch {
        openSnackbar({
          severity: 'error',
          message: intl.formatMessage({
            defaultMessage: 'Error updating date of birth',
          }),
        });
        return;
      }

      openSnackbar({
        message: oldDob
          ? intl.formatMessage(
              {
                defaultMessage:
                  'Date of birth has been changed from {oldDob} to {newDob}',
              },
              {
                newDob,
                oldDob,
              },
            )
          : intl.formatMessage(
              {
                defaultMessage: 'Date of birth has been changed to {newDob}',
              },
              {
                newDob,
              },
            ),
        action: () => {
          updateCandidateDetails({ dateOfBirth: oldDob });
        },
      });
    }
  };

The component Date of birth组件 出生日期

 <DetailsItem
          title={intl.formatMessage({ defaultMessage: 'Date of birth:' })}
        >
          <DateOfBirth
            dateOfBirth={candidate.dateOfBirth}
            onEdit={onEditDateOfBirth}
          />
        </DetailsItem>

Months utility月效用

import { enUS } from 'date-fns/locale';

const monthsList = () => {
  const months = [];
  for (let i = 0; i < 12; i++) {
    months.push({
      monthString: enUS.localize.month(i, { width: 'abbreviated' }),
      monthNumber: i + 1,
    });
  }
  return months;
};

export default monthsList;

If anything still missing please comment and I'll add如果还有什么遗漏,请评论,我会补充的

When user clicks on submit button, you should call a function and read the values of inputs and validate them.当用户单击提交按钮时,您应该调用 function 并读取输入值并验证它们。 If there is any error, you set a state indicating an error;如果有任何错误,则设置一个 state 指示错误; let's say you call setError(true) .假设您调用setError(true) Also, there is a prop called error on your inputs which when they are true , their UI will change so that user understands there is an error.此外,您的输入上有一个名为error的道具,当它们为true时,它们的 UI 会发生变化,以便用户了解存在错误。 You can also store the text you want to show when an error occurs in a state and display it accordingly.您还可以存储 state 中发生错误时要显示的文本并相应地显示。 Remember to remove error text and set error state to false when there is no error.没有错误时,请记住删除错误文本并将error state 设置为 false。

See mui documentation for more information.有关更多信息,请参阅 mui 文档。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如果结束日期小于反应中的开始日期,如何验证小于反应开始日期的结束日期并在横幅中添加错误消息 - How to validate End date which is less than the Start date in react and add error message in banner if the end date is less than start date in react 如果输入不是 js 中的有效日期,如何连接/更改错误消息? - How to concatenate/change the error message if the input is not a valid date in js? 如何验证日期输入? - How to validate a date input? 如何验证输入是否为日期 - How to validate that an input is a Date 如何验证 Javascript 中的单个/多个图像并在输入下方显示错误消息以防止默认提交? - How to validate an single/multiple image in Javascript and show error message below the input with prevent default submit? 如何验证我的文本字段输入? - How to validate my textfields input? 无论日期格式如何,如何使用JavaScript验证输入字段? - How to validate a input-field with JavaScript no matter which date format it is? 如何在3个字段中验证日期输入? - How to validate date input in 3 fields? 如何验证输入的日期格式是否有效? - How to validate entered date format is valid or not? 验证php文件中的开始日期和结束日期时出错 - Error to validate the start date and end date in php file
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM