How should we handle linking up forms with a redux store in polymer 3?
This gist is my attempt to do a couple things:
- Articulate the problem
- Begin aggregating relevant existing solutions or things we could use
- Define desired solutions that may or may not exist in usable forms
I'd consider a robust implementation of forms + redux to ultimately account for the following (meaning, it doesn't need to happen in redux, but redux should be friendly to it):
- When the form is first added to the page/connected with store
- When the user types something into a single input
- immediate input validation
- remote input validation
- assigning arbitrary # of custom validators
- value coercion
- When the user types something into something related to a "part"
- immediate validation across all inputs related to a "part"
- remote input validation
- assigning arbitrary # of custom validators
- value coercion
- UX
- dirty-checking (so not all red messages at once)
- maintaining accessibility
- maintaining i18n
- validation messages/form interaction
- Submit validation
- displaying validation messages from server
- A convention by which to map back-end server validations directly into front-end code, so the two places can stay in sync
- Developer UX
- declarative form-writing
- intuitive, easy-to-understand state
- form lifecycle methods
- conventions around things that happen all the time
Finally, I'd suggest, client-side validation is more a UX question than an actual data validation question.
Probably the biggest one is https://redux-form.com/7.3.0/... but this is tied extremely closely to the react ecosystem.
Native html form validation logic: https://developer.mozilla.org/en-US/docs/Learn/HTML/Forms/Form_validation
Constraint validation api: https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#the-constraint-validation-api
MWC Form validation logic: ???
This looks super interesting: https://rickharrison.github.io/validate.js/ -> 2kb for dependency-free form validation on all browsers
We could go the complete opposite direction and consider form local state.
<form
???
In the redux store, there's a special place in the store: forms
. This is a large array that holds all the forms a user interacts with. Here's what usage would look like:
- user navigates to a page with a form on it
- we see this. BUILD_FORM action is dispatched, grabbing the next available index of the
forms
array. This index is then uniquely correlated with the form on the page:- perhaps a k/v store with different key generation strat?
- index-based approach allows for straight up duplicates of forms with unique identities.
// initialValues - whatever the initial representation of the state of this form is
// customValidators - logic that can run against a specific value
Right away, I see the need to rework the structure of the top-level forms
state:
forms: {
instances: [], // the array of forms
rules: [], // array of validation rules or other declared behavior used by form
}
and then each instance of a form:
// forms.instances[0]
{
initialData: {}, // what it started with
data: {}, // what it currently is
rules: {}
}
So, here's where things get a little mucky. I like the simplicity of the approach that:
a given input has a name
, and value
. name
is responsible for mapping directly to the place in the data structure the given value
is stored
(ofc you can swap these out later)
So this could mean:
<input type="text" name="dynamicallyAddedFields[1][description]" value="whatever"></input>
would correspond with:
data = {
name: 'something',
dynamicallyAddedFields: [
{
description: 'something else',
},
{
description: 'whatever'
}
]
}
Why include this 'path' to the specific place? So you can declaratively store validation behavior for a specific place in your data in validations
. Here's where you would declare the validations:
validations: {
'dynamicallyAddedFieldName[1][description]': {
min_length: 5
}
}
But it's right here that I think it would be good to differentiate some things. Consider there are different levels of validation at different points in time:
- Input Validation (soon as user types in)
- Multiple Inputs in a form that need to be validated together
- Remote Validation
- Value Coercion
- Submit validation (mapping errors from server to user)
So I think it would be useful to be able to define places in the form that aren't simply a single field
, but an arbitrary part
.
A form part is a place you can drop into your form that will:
- Handle all the same behavior that happens for a single field, but across multiple fields.
So if you need remote validation on the validity of a combination of fields which can only be determined by the server, here's where that might happen.
If the form submission happens successfully, the existing place for it in forms.instances
is wiped. Then it's repopulated by a new one.
- - how to make this declarative via html?
- - form lifecycle methods
- - state design for form parts
- - action/reducer design to allow ability to quickly do things by convention
- - mapping api to translate BE-validations into FE-compatible validations
- - how to deal dynamically adding inputs to an existing form (for ex in sub-sub-sub-components)