Reputation: 318518
I have a field in react-final-form where the user enters a date. For the internal value this gets normalized to YYYY-MM-DD
but the user may enter it as DD.MM.YYYY
.
For valid data this is all fine, I can use parse
to normalize and format
to convert back.
However, if a user enters garbage, there's not much I can do in parse
... I ended up doing this awful hack which works, but I wonder if there's a cleaner way that allows me to separate the parsed data that will be fed into the form values, and the data that will be used to display the component and validate the user input.
const formatDate = (value) => {
// console.log(`format: ${value}`);
if (!value) {
return '';
}
// something invalid => keep as-is
if (value.startsWith('INVALID:')) {
return value.substr(8);
}
// we have a valid value => format using our preferred display format
const m = value.match(/^(\d{4})-(\d{2})-(\d{2})$/);
return `${m[3]}.${m[2]}.${m[1]}`;
};
const parseDate = (value) => {
if (!value) {
return undefined;
}
// console.log(`parse: ${value}`);
const m = value.match(/^(\d{2})\.(\d{2})\.(\d{4})$/);
if (m) {
return `${m[3]}-${m[2]}-${m[1]}`;
}
return 'INVALID:' + value;
};
const validateDate = (value) => {
// console.log(`validate: ${value}`);
if (value && value.startsWith('INVALID:')) {
return 'Invalid date';
}
};
<Field
name="date"
component="input"
type="text"
format={formatDate}
parse={parseDate}
validate={validateDate}
placeholder="dd.mm.yyyy"
/>
Here's an executable codesandbox: https://codesandbox.io/s/react-final-form-format-on-blur-example-forked-5oowz?file=/src/index.js
Note: I'm NOT looking for date pickers or similar widgets that would rely on the field not being directly editable.
Another kind of field where the current behavior feels a bit lacking is for number inputs:
Upvotes: 1
Views: 512
Reputation: 3259
You may keep both raw and parsed strings as a value for the field:
const formatDate = (value) => {
// console.log(`format: ${value}`);
if (!value) {
return "";
}
// something invalid => keep as-is
if (!value.parsed) {
return value.raw;
}
// we have a valid value => format using our preferred display format
const m = value.parsed.match(/^(\d{4})-(\d{2})-(\d{2})$/);
return `${m[3]}.${m[2]}.${m[1]}`;
};
const parseDate = (value) => {
if (!value) {
return undefined;
}
// console.log(`parse: ${value}`);
const m = value.match(/^(\d{2})\.(\d{2})\.(\d{4})$/);
if (m) {
return { parsed: `${m[3]}-${m[2]}-${m[1]}`, raw: value };
}
return { parsed: null, raw: value };
};
const validateDate = (value) => {
// console.log(`validate: ${value}`);
if (value && !value.parsed) {
return "Invalid date";
}
};
So the value of the field is actually an object of shape { raw:string, parsed:string}
. When parsed
is empty means the date is invalid.
Upvotes: 0