68 lines
5.5 KiB
Markdown
68 lines
5.5 KiB
Markdown
|
|
# Forms
|
||
|
|
|
||
|
|
The reservation is mostly created by filling in a form. For that reason this plugin has it's own _form definition_ feature.
|
||
|
|
|
||
|
|
The form definition is an array of element it contains. Each element has a *type*, for example: `input-text`, `input-reservation`, etc.
|
||
|
|
|
||
|
|
Keep the form structure and content separate -> when working with the form, take time to figure, if you are working with structure, or existence of something within.
|
||
|
|
|
||
|
|
## Submitting
|
||
|
|
|
||
|
|
When user submits the form, the `RsvFormSubmitter.js` is called. It collects the values, which we describe in more detail, and then sends it using `fetch` as POST to `reservations/forms/{form_id}`.
|
||
|
|
|
||
|
|
### Collecting
|
||
|
|
|
||
|
|
The *collecting* is a process with `<form>` DOM subtree as an _input_ and valid input JSON for the defined form at `form_id` as _output_.
|
||
|
|
|
||
|
|
We decided to only consider *linearized* form, therefore an single-dimension array of fields. We find little to no value to define JSON structure in the form itself, but rather define linear array of inputs, where each input value is a JSON itself. We explaing why that is beneficial.
|
||
|
|
|
||
|
|
1. Even atomic values like `"hello"` or `69` are valid JSON format. This means the collector can assume every value is a JSON and use `JSON.parse(input.value)`.
|
||
|
|
2. We can change the form structure, but the output remains the same. The use-case is for example, grouping fields for First and Last name into one row, but still keep them separate attributes in final JSON. On the other hand, the country code and telephone number are two fields on the same row, but should be one attribute in the final JSON. Both cases reflect only in the DOM structure.
|
||
|
|
|
||
|
|
### Contract
|
||
|
|
|
||
|
|
For element to be collected, it has to comply to a contract. Namely, it must have a `rsv-form-field` class and have a `value` attribute. The tagging with `rsv-form-field` class allows that by default, no custom defined component is collected. Therefore you can compose new elements from existing ones and only tag the outer-most as `rsv-form-field`. For example:
|
||
|
|
|
||
|
|
```
|
||
|
|
<rsv-reservation-collector class="rsv-form-field">
|
||
|
|
<rsv-calendar/>
|
||
|
|
|
||
|
|
<rsv-time-slot-selector/>
|
||
|
|
</rsv-reservation-collector>
|
||
|
|
```
|
||
|
|
|
||
|
|
If the collector would collect exact elements, like `<input>`, it would:
|
||
|
|
a) require a register
|
||
|
|
b) be unpredictable
|
||
|
|
c) require some form of filtering
|
||
|
|
|
||
|
|
## Element handlers
|
||
|
|
|
||
|
|
Element handler is a PHP class extending `RsvFormElementHandler` class. The parent class has two abstract methods: `draw` & `submit`. The `draw` method is called when the form is being rendered on the backend for the user. The caller is the `RsvFormRenderer` that does not try hard to catch all errors, so be careful with putting logic to `draw`.
|
||
|
|
|
||
|
|
The other method `submit` is called by the `RsvFormProcessor`. It definitely should validate the value, but it can also do other things. For example, the element for reservation saves the reservation to the database.
|
||
|
|
|
||
|
|
You might have noticed in a reservation example one major flaw. What happens, when any of the next elements fail and cause the whole form _unworthy of submission_? The error handling itself and propagating back to the user is described later. For now let's focus on handling the error correctly.
|
||
|
|
|
||
|
|
We thought of two approaches: separate validation & submission steps and rollback. The first approach will not actually solve the issue. It might eliminate some cases, but sometimes error slips through and cause exception in the submission step. For example suppose creating reservation. The validation step can decide it is okay and the time block is available. But right after that another request creates the reservation in the same time block.
|
||
|
|
|
||
|
|
We could either do same validation in the submission step, but then, what is the point of the validation step. Another solution is to create a token for the time block. The second solution requires one important thing, the element must know it is an element, to implement the safety gates correctly.
|
||
|
|
|
||
|
|
The second approach is using a rollback, that does so when any of the next elements fail and cause the whole form _unworthy of submission_. This way only the element handler has to implement the safe gate.
|
||
|
|
|
||
|
|
TODO: generalize it using a property the element submission must have
|
||
|
|
|
||
|
|
## Register
|
||
|
|
|
||
|
|
Register is a string to object mapping, that maps element IDs to instantiated objects of the handlers. This allows to customize the handler by changing it's state. For example, there is a handler for input text. The constructor can have a predicate lambda that does the validation and allow simple implementation of email, telephone number or any other validation. Or it can be wrapped with regular expression.
|
||
|
|
|
||
|
|
## Error handling
|
||
|
|
|
||
|
|
When an error occurs, the backend should send back an error response, so that user knowns what he did wrong. The response should contain element's ID, that detected the issue and error ID. The reason why not use a message is for internationalization. The form processor does not know and must not know the user's culture. The internationalization is a separate concern and should be done mapping error ID to a particular message.
|
||
|
|
|
||
|
|
Last thing inspired by Problem Details RFC are extensions. The response can contain a dictionary mapping strings to strings that contains structured data about the error. For example, only one time slot of multiple can be occupied. The extensions could contain value of the occupied time slot and the response handler could use it to provide a more detailed error message.
|
||
|
|
|
||
|
|
## Success handling
|
||
|
|
|
||
|
|
Even success must be handled. The user must know that the submission is successfully finished. The form definition can contain a success message as HTML.
|