Closed jaredpalmer closed 4 years ago
This would be game changing
For now I did such stuff in my custom fields, adjusting onChange
and value
, would be cool to have it built-in!
This is exactly what I need right now. I've got lots of glue code in unreliable places converting ids to numbers all over the place as it stands.
@jaredpalmer this is what I meant by suggesting a valueConverter callback, but these names make so much more sense π
I'd like to suggest basing this off of #1334 so parse and format have access to real types in TypeScript. Also to recap something I was thinking about as part of this, it'd be nice to not require the parse / format props when a field has a string value (since html inputs are always strings unless you hackishly modify their value), but to require it when a field has a different value type. I couldn't figure out how to make TS+React play nicely there.
Also, I'm not sure if React Native inputs always use strings because I'm overall not familiar with that.
https://joepuzzo.github.io/informed/?path=/story/formatting--format-and-parse If you want to take a look at how I've implemented format + parse, and mask ( Might be helpful )
I'm currently implementing a credit card payment form and was looking at how Stripe forms behave and they are doing pretty complex stuff with setting cursor position etc. especially with the expiration date input. E.g. when you type 10
, it will display 10 /
and move the cursor at the end, so that you can enter the year. If you then press backspace, it will display 10
, removing /
at the end. In both cases, the parsed value for month would be 10
(unless they don't parse incomplete fields at all), but the formatted (i.e. displayed) value is different, once it's 10 /
and once it's 10
. So it looks like for such use case, format
function would not only need access to the current values, but to the last event as well. Another issue is that one input provides 2 values - expiration month and year.
However, maybe parse
and format
props don't have to support such complex use cases.
Something like this https://joepuzzo.github.io/informed/?path=/story/formatting--mask-with-cursor-offset ?
@joepuzzo Yeah, though I'm not sure if you can replicate Stripe behavior exactly, if the mask
function doesn't additionally have access to the event that triggered the change, because as I described above, for the same input value (e.g. 10
), it will display either 10 /
or 10
.
You can play with Stripe forms here: https://stripe.dev/elements-examples/
Interesting! The parse callback would definitely have access to the last value since it could check event.target.value
which would be the old raw value before setFormikValue()
is called. In this event, if the internal representation of the field in Formik is a string, the parse function can return 10/
or 10
depending on the circumstance. However, if the internal representation in formik is month: number
, year: number
, then something special would have to happen. For example, if the internal representation of the field is an object, it could like look like: { month: number, year: number, isLeading: boolean }
.
Overall I think the stripe example could work with a simple API like this, but exposing the last value to the format function can also be useful, for example if we need to compare current value and last value to determine the index of the input cursor. It could be "noted" by the parse function and used later by returning an object like { value, cursorIndex }
. A lot of masking breaks down when the user starts to delete from the middle like changing a middle number in their phone number, or when selecting multiple characters + deleting or typing. I'm interested to see if anyone has more experience with that functionality specifically, or if it's outside the scope of this API.
Edit: nevermind apparently that part is covered above :P
@johnrom I'm using https://github.com/medipass/react-payment-inputs with Formik to handle a credit card form and while looking into fixing some of its issues (https://github.com/medipass/react-payment-inputs/issues/2) I arrived at similar conclusions ;) This library does not handle parsing (or at least it does not expose the parsed values to Formik, though it needs to parse them internally to validate them). I had to additionally pass the previous value to its formatExpiry
function, which currently only accepts an event. I had to return not only the formatted value, but also, optionally, the new caret position, to handle cases like e.g. when you got value 02 /
and try to delete 0
- in such case Stripe does not delete 0
, only moves the caret to the beginning.
@szimek maskWithCursorOffset
does use the event to track the location so it is possible. If you look at that example it does exactly that
Hello all, as we're discussing adding a way to manipulate a field's internal value vs the displayed value, what are your thoughts about manipulating a field's value before it gets passed to onSubmit
?
My typical use case is for parsing numbers for example, I always have to do it in a centralized manner in onSubmit, but it could be done at the field level so we can drop components like NumberField
directly into a Form
and get the expected value in onSubmit
.
I asked about it on final-form's repo : https://github.com/final-form/final-form/issues/242#issuecomment-524868187 but I'd love to have other opinions.
@jgoux this would be done via onParse
in the API above, meaning Formik's internal value would match that which you expect to submit. So if you did onParse={convertStringToNumberCallback}
and onFormat={convertNumberToStringCallback}
, your submission would always have the numeric value and not the string value.
@johnrom The issue I have with using onParse and onFormat for this purpose is that you have to reimplement all the browser native logic to parse and format a number.
For example, what do you expect to see when the user is typing a float like 2.
? You'd have to track the separator in order to format it correctly. Or if you type 13.0
, how do you keep track of the last 0
in order to go to 13.08
?
This doesn't seem much but it's for this kind of things that we have input
types other than text
π
What I want is to rely as much as possible on the browser, and just apply a transformation when the user is done filling the form, but at the field level. Does that make sense?
@jgoux You bring up an interesting point. There are two different types of parsing we are talking about here. One reacts to a user's typing, formats it for their own UX, and prints it back out at them (like phone number masking), and another takes the internal representation and formats it for submission to the server (maybe the server actually just wants 5555555555
). I'm not sure whether we should solve them both at once or keep separate issues for them.
I solved this issue in informed. Itβs totally doable. https://joepuzzo.github.io/informed/?path=/story/formatting--format-and-parse
Yes, I have 2 use cases that make me think this is a valid requirement from a form validator.
First use case:
I have a tuition
Field
which utilizes a number formatter. But the problem is that API requires the tuition as an integer without ,
s.
My approach to this problem was using state
as the value of tuition
with ,
and manually setFieldValue
by my self.
Second use case:
I'm using material-ui-picker
DatePicker as a formik Field
everything goes right except API requires me to give the date in YYYY-MM-DD HH:mm:ss
format instead of a timestep.
My solution was like use case 1 but again I thought there should be an easier way for this common problem
Apologies for the bump, but this functionality would be extremely useful to have. Is it still on the cards?
I'm very interested in this functionality, just haven't had any time to implement.
I solved this issue in informed. Itβs totally doable. https://joepuzzo.github.io/informed/?path=/story/formatting--format-and-parse
@joepuzzo So does this only work with the Informed library or have you also figured out how to make it work with Formik?
I have format/normalize hacked into Formik 1 using a custom Field that renders a Formik Field but trying to get it into Formik 2 now and struggling. Formik Material UI library complicates this because they don't pass down onChange, value, etc.:
https://stackworx.github.io/formik-material-ui/docs/guide/faq
@MikeSuiter To my knowledge its only an informed thing. I maintain Informed and not Formik so I am unsure. All this being said, informed does everything that Formik does so using it as an alternative should be fine :)
I create wrappers for Field myself, pass them Field
, and do onParse
and onFormat
myself, and it works fine. It would definitely be a bonus if it was implemented in Formik itself, but on the TypeScript side it would depend pretty heavily on strongly typed fields (#1334). I built my project off of this PR (#1336) which implements typed fields, but it's not based off of v2.
I assume there are some non-trivial complexities and api choices involved in adding the value cycle for format and parse. And I assume those choices might lock things in, so I would like to just add that I think it would be very nice if format and parse could be implemented in a way that _input masking could be added too later_ .
The common use-case I have is a date field with DD-MM-YYYY input, where I would like the dashes to be automatically entered as the user types, and the initial and submit value to use YYYY-MM-DD.
So eventually to be able to create a standalone <DateField name='..' ../>
component without form-level machination, using formik's parse
, format
, mask(?)
field props.
If I remember correctly redux-form can't do this either. normalize
can be misused for masking but then it's not possible/easy to use parse
and format
alongside for this use-case.
Came here looking for this very solution. π Looking forward to seeing the PR merged.
Here is my solution to add a parse function.
import React from 'react';
import { Field, useField } from 'formik';
export default function MyFormatField ({name, validate, parser, ...others}) {
const [,, { setValue }] = useField({name, validate});
return (
<Field
name={name}
onChange={event => {setValue(parser(event.target.value))}}
{...others}
/>)
};
This is now available in 3.0.0-next
Closing as this is going to get released in upcoming major i.e. 3.0.0
@johnrom do you have any examples of what you did in the past you could share?
https://github.com/formium/formik/issues/1525#issuecomment-578950642
Yes please. I am curious about useField API. Do you have anything on your mind already?