There are a lot of very complex forms in our applications. Complex forms does not mean just complex validation, but we fill form fields automagically or change editable state based on other form fields’ values. This needs a powerful form model. If you try to implement it with event handlers then it will certainly cause you some serious headaches.
Consider the following simplified invoice model:
Field | Type |
---|---|
partnerid | ID |
paymet | ID |
invdate | Date |
due_days | Integer |
duedate | Date |
productid | ID |
qty | Number |
price | Number |
vat | Number |
grossprice | Number |
In this simple example the user should fill in three fields, all the others can be determined, or defaulted from these values. Every partner has a default payment method and if it’s payment method is credit, then usually it has a payment deadline in days (due_days) determined by a contract. Invdate is usually the actual day, duedate is calculated from invdate and due_days.
The selected product and the customer usually determines the price, VAT and grossprice. So if the user specifies the partner, product and quantity the application can fill all the other fields. However, we want the user to be able to change any other values.
Do not bother if you couldn’t follow everything, just read forward. Or you can jump to the form demo below.
Challenges
This problem is very complex on different levels.
First of all, the auto-fill of the fields can be very confusing to users. If the price field depends on the product selected, then the onChange handler of the product field have to set the price field. But what happens if the user modifies the price value and then she selects an other product? What should the program do? Should it set the price value to the product’s price, overwriting the previously set value?
The next problem is the implementation. This is a very difficult and error-prone process, it involves a lot of onChange handlers with a lot of dependencies, recursive field auto-fills, etc.
I have searched for a good form model we could use, but I couldn’t find anything beyond validation libraries. I thought a lot about the problem and I have come up with a possible solution.
Suggested solution
We can design a reactive form model with defining small functional rules between the fields. Complement our model with expressions that calculate a default value for the fields.
Field | Type | Value |
---|---|---|
partnerid | ID | - |
paymet | ID | = partners[partnerid].paymet |
invdate | Date | = ‘today’ |
due_days | Integer | = partners[partnerid].due_day |
duedate | Date | = invdate + due_days |
productid | ID | - |
qty | Number | = 1 |
price | Number | = priceCalc(productid, partnerid) |
vat | Number | = products[productid].vat |
grossprice | Number | = price * ( 1 + vat / 100 ) |
This model can be easily implemented in a reactive environment (for example in a spreadsheet :) ). We want the user to be able to change the values, so we implement some field states. A field can be in four states:
State | Sub-state | Appearance |
---|---|---|
AUTO | Gray | |
CHANGED | OK | Green |
CHANGED | WARN | Yellow |
INVALID | Red |
The AUTO state means the user haven’t changed the value of the field, it shows a calculated default value (or it can be empty). If the user changes the value of a field, it gets into the CHANGED state. The WARN sub-state can alert the user that the fields calculated value and the user value differ. The INVALID state of course indicates a validation error.
If we give the user a way to control the CHANGED state, then we get a consistent GUI. See this demo below, it is not perfect, but you can get a feel of the concept. You should at least set the customer and the product. Then every other field gets a default value and you can play with it.
Demo
Implementation
I am considering to create an open-source JavaScript library to implement this. The library would get a model consisting of:
- Field descriptions with type definitions, type constraints (considering the use of tcomb)
- Value links with reactive rules (field auto-fill)
- State links with reactive rules (visibility, editability, optionality)
- Validation rules (for complex inter-field validation constraints)
and it should handle any data interaction with the form.
Here are my expectations about the library:
- It should work in the browser and on the server side.
- It should work well with React.
- It should be able to handle all form interaction on the client side:
- Form auto fill
- All sorts of validation
- Field-level user permissions
- Toggle field properties (visibility, editability, optionality) based on form state
- It should be able to handle server side API validation
- It should have slight dependencies (for example it should not depend on React, but it will have some React helpers and components in a separate library)
I would appreciate any thoughts, comments.
You can look at the source of the above example. This is a work in progress, so I don’t bother myself with documenting it in detail, but a lot of things are self explanatory:
|
|