[英]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.