Dependent forms
Forms can be dependent on each other. For example, you might want to show or hide certain fields based on the value of another field above. Or show an entirely different sub-form based on a selection. Both are equally possible.
Defining dependent forms
The first design choice is to what value the final form should return, which should contain all information. This is can be done with records with optional fields or discriminated unions.
For example, consider a form where the user can select between two options, to enter information about a teacher or a student. Based on the selection, we will show a different sub-form.
First we define our data type, and the form that chooses between the two options.
For the latter, because we only have two options, we can use a bool value for simplicity, but this could be expanded to use unions too for more options.
The StudentForm is already prepared to work with a WebSharper.UI.Client.CheckedInput<int> value internally which is produced by UI helpers to aid validating and parsing nubmer inputs.
Then if the value passes validators, it is safe to map it to an int.
The next step is to take a look at the signatures of TeacherForm and StudentForm to see what arguments their render function takes.
For TeacherForm it is Var<Subject>, and for StudentForm it is Var<CheckedInput<int>>.
We need to create a helper union type that can switch between these two reactive vars, because to create a dependent form, we need to have a uniform input side too.
Now, we are ready to use the Form.Dependent function to show a different sub-form based on the value of the IsTeacherForm.
On both sub-forms, we use Form.MapRenderArgs to unify the types of the input side, and Form.Map to unify the types of the form result.
This sets things up well for both rendering and running the form, where the types themselves will guide writing correct code.
Using Form.Do computation expression
Alternatively, you can use the Form.Do computation expression to achieve the same result.
This can be more readable, especially when you have multiple dependent forms, it avoids nested lambdas, but otherwise it's syntax sugar only.
Every use of let! will create a dependent form.
We use return! to return a final form without an extra layer of dependent forms, as return would only allow to return a form result value.
Rendering dependent forms
When rendering dependent forms, we can start with Forms.Render as with other forms.
The rendering function will get a value of type Form.Dependent<_,_,_>, which enables rendering the two sub-forms, called primary and dependent.
In the rendering function, we use dep.RenderPrimary to render the first form, which selects between student and teacher.
Then we use dep.RenderDependent to render the sub-form based on the selection. The submit.View.Through rvYear is used to show validation messages for the student form's input.
Complete example
Here is now the complete example, showcasing all the elements described in this tutorial.