Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

parse and format confusion (and wrong API?) #229

Closed
faassen opened this issue Mar 2, 2017 · 6 comments
Closed

parse and format confusion (and wrong API?) #229

faassen opened this issue Mar 2, 2017 · 6 comments

Comments

@faassen
Copy link

faassen commented Mar 2, 2017

In the API docs you write:

parse: from input to store.
format: from store to output.

I think by "input" you mean the values parameter that goes into the form object. By "output" you mean what I get when the form is submitted.

It doesn't seem to work properly (#228).

Then you give example where a number is parsed, and a string is formatted. But that's the wrong way around.

I think the API is wrong and very confusing. First something about the meaning of the word "parse" and "format":

You parse strings. That's part of the definition of the word "parse". You don't parse numbers or date objects, just strings. So I could parse "2017-01-01" into a date object. Parsing can fail due to bad user input. For instance, if the user inputs "2017-broken-date" parsing should fail. You have two options: either make sure users validate the input string before they even start parsing it, or allow the parser to return or raise an error message that gets displayed in the UI.

You format objects into string. So if I have a date object representing 2017-01-01 I could format it into the string "2017-01-01". Formatting never fails unless there's an actual bug.

But in this API, you get to format a string and parse a number. That needs to be fixed as that's definitely wrong by the definition of those words.

Now as to the API itself. Here are some usage scenarios:

Input/output conversion

  • I input a date field into the form as a date object as "values". Before display, it should be formatted to
    a date string.

  • When the user shows or changes the date, the date string is changed in the store.

  • When the data is submitted, the output is a string, which should be parsed into a date object.

I think this is what you implemented (though see my comment about how the terms parse/format are to be used).

I expected the following scenario instead.

Objects everywhere

  • I input a date object into the form as "values".

  • When it needs to be displayed, it's formatted as a date string.

  • When the user changes it, it's parsed into a date object (or an error message is displayed).

  • I can create validators that use this internal date value. For dates I could compare it with another
    date, for instance. I can also set the value as a date object. In general any interaction with the value is
    as a date, not as a string.

  • When the form is submitted, I get the date objects, no strings.

This use case allows the store to use any representation it likes, and I can reason in terms of that representation. Only for display in a field the object is formatted into a string, and only when that field is changed is the input parsed into the underlying object.

Another use case would be to convert empty input to the null value automatically, which is handy
if the server expects null in such cases.

Allowing the store to maintain objects instead of only strings is a big change. But I think ultimately it makes for a cleaner and more powerful form library.

JSON serialization

There's another usage scenario. JSON, like a form, often needs parse/format behavior too. If I have a date object, I want it to be represented as a date string. So when I get JSON data I want to put it into the form I may need to parse some of it first, and when I submit the form, I may want to serialize it into JSON data right away. It would be nice if I could use the same parsers to convert JSON input to internal values and the same formatters to convert internal values to JSON output. This would require an additional API for the form that deals in terms of JSON as opposed to JS objects (jsonValues for input and output).

@foxhound87
Copy link
Owner

foxhound87 commented Mar 6, 2017

I understand your point of view.

The problem is we need to manipulate data in different situations:

  • when the form is initialized with initial data
  • when the user change the data in the inputs
  • when the user retrieve the data
  • when we need to validate the data

then another problem is the Field value (which is a mobx computed) which can be a different type for each of the situations in the previous list (and now it is always the same).

One solution can be to create a parsed and formatted computed values, which can be used respectively on each situation.

What's your suggestion?
Thank You.

@foxhound87
Copy link
Owner

foxhound87 commented Mar 13, 2017

Anyway, I should first invert the parse/format functions.

@foxhound87
Copy link
Owner

foxhound87 commented Mar 13, 2017

A solution can be to apply format on a new display computed prop and apply parse on the current value computed prop. Then we can use display to rewrite the props bindings for the components value.

@faassen
Copy link
Author

faassen commented Mar 15, 2017

Here is how I see it (it may be wrong):

Data you see in the form is mostly strings. This data is retrieved by the user for display, and it is given back to the user when an onChange happens.

Currently mobx-react-form allows you to validate those strings. That makes sense.

New would be the parsed (or converted) data. You may want to apply another validator to converted data when it's more convenient (example use cases: "this number needs to be larger than 10", or "this date needs to be before this other date"). This might become a parsed prop you can interact with on the field, or as you suggest a display prop (which I think is nicer as you expect a parsed value to be possible for "value").

Not all use cases need the inverse "format" -- the parsed data may be maintained next to the string version automatically when changes happen. But if we want users to be able to initialize the form with parsed data then a "format" is definitely needed. Similarly formatting may be useful if you write code that changes the form (for instance when fields are dependent on each other). This is for similar reasons as why you want to validate the parsed/converted data.

Then there's data as serialized to send or receive from a server. That's typically JSON. It uses strings in some cases (for dates or decimals for instance), but it can use types for numbers and booleans and "null". I think it interesting to explore this use cases. While mobx-react-form is not responsible for server communication, conversion to and from JSON is so common a use case I think it would be nice to support it. This form of data implies another type of parse/format behavior, form -> json and json -> form. I think it might be interesting to consider a json method (or computed prop) and a way to pass json directly in the form.

Then there's the question about sensible defaults. What data parser/formatter is active by default? I think none -- the behavior is as it is today. The same for the json parser/formatter. But you can plug in parse and format functions and jsonParse and jsonFormat functions into fields.

There's another use case I want to mention. I don't think it's urgent, but I think it's useful to consider in all this. A single value could be determined by multiple fields. In a way, a subform (and repeating subform) is like this: multiple fields in the subform result in a single object. But perhaps we can generalize this, so that a developer can plug in a function that "formats" a single value in the "values" input into a bunch of separate fields in the form, and similarly a way to parse those multiple field values into a single output value again.

@faassen
Copy link
Author

faassen commented Mar 15, 2017

So concretely: value would be the calculated value. display would be the formatted value. By default it's all strings, but the user can plug in a parse function for the field to change the behavior. The user can also plug in a format function. (it's recommended to implement both).

"values" when inputted into the form are in parsed format. When you get them out again upon submission, you get the parsed values. But maybe a new API is needed to get the display values.

You can also define a jsonFormat and jsonParse function per field. You can pass in jsonValues which use jsonParse and access the json representation of the form during form submission, which uses jsonFormat. If you don't implement them they just give back the value (the user might get unserializable JSON but they use another strategy like implementing toJSON on the object).

@foxhound87
Copy link
Owner

Closing this.
Converters are now called ‘input’ and ‘output’

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants