Forms and Validation (Version 2) #8
Andrei15193
announced in
Guides and Tutorials
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
Forms are a central element to any application, we always have to handle user input which can come in a number of ways. We have text inputs, date pickers, number fields, dropdowns and so on.
For each field on a form we may need configure validation rules, these is generally done through callbacks that can be reused for multiple fields as well as having specific one for particular fields.
In this tutorial we will define a simple application for storing a list of books. We will have an add form requesting us to add the title, ISBN and authors of a book. Display all of our books in a list that we can filter.
To get started, configure a project to run React with TypeScript and add
react-model-view-viewmodel
as a dependency. Or better yet, start a sandbox based on the React MVVM Template on CodeSandbox: https://codesandbox.io/s/react-mvvm-vwsqlv.For simplicity, we will store the books in
localStorage
as an array, useJSON.parse
andJSON.stringify
for serialization.Routes
We will be having two pages for our application. One for displaying the list of books which is the landing page, the second one is for adding a book.
Form Structure
React MVVM comes with two core concepts regarding forms, one is the
FormFieldViewModel<TValue>
which represents a single field. It can contain any type of value. The binding is done in a React component as well as the mapping between the input value and the field value.The second concept is
FormFieldCollectionViewModel<TFormFieldViewModel>
which, as the name suggests, contains a set of fields. The form field collection is essentially the form itself.We get a number of useful features when we use the two together, such as checking if the entire for is valid or going through all fields for processing.
In our case, we will define our form with two text fields and a collection field. The first two for the title and ISBN and the last one for the authors.
The
FormFieldCollectionViewModel.registerField
adds the field to the collection. This will make our ViewModel aware of changes happening with each individual field, mostly for the validation states. If at least one field is invalid then the entire form is invalid.Form Presentation
We have our form structure, now it's time to define the user interface for it. Our form only has text inputs that work with a
FormFieldViewModel<TValue>
, we can define a component for handling just one input.We want to bind the ViewModel to the component and display a label so we know what we are editing.
The binding is done using the
onChange
event, whenever the text changes we update the viewmodel. We can opt for different options such as updating the ViewModel when the input loses focus.This is a two-way binding, in case the value of our field changes from some other area of the application or we just want to ensure that whatever is in the ViewModel is displayed on the form as well. We can opt for one-way to source binding by using the
defaultValue
attribute instead ofvalue
as well as one-way binding by not updating the ViewModel.Depending on the application and desired user interaction, we can use one way or the other. The library does not make restrictions on how binding is done or what its limits are. Remember to observe the ViewModel for changes using the
useViewModel
hook!The next step is to add the entire form definition. For the two fields this is easy, we only pass our fields to the newly defined
TextInput
component.For the authors we will use an intermediary field that is defined at the component level, when we press a button we want to get what was written in the
TextInput
next to it, add it as an author and then clear the input.If we use the
useViewModel
hook we will end up with a new instance for each render as we pass the field config as an object and a new object is being created for each call. On one hand, we can store the field config outside in aconst
or we can use theuseViewModelMemo
hook.Submitting the Form
This is working great so far, only one issue, we need to be able to actually add the book in our collection.
We will add an
add
method in our ViewModel which first checks if the form is valid, this is generally a good practice even if we do not have any validation. This will ensure the form will be submitted only if it is valid even if we add validation later.As mentioned, for simplicity, we will use
localStorage
to store our data.Our book data transfer object is quite simple.
Towards the bottom of the form, we will add our buttons.
Whenever we add a book or want to go to the book collection we navigate to the landing page, which we have not yet defined...
The Book Collection
This component will be fairly easy, it is very similar to what we have done in API Calls & Displaying Data.
At the top of the list we will display an input for filtering the books by title, below it we will display the books. Having an empty input will show all books.
Now to define our ViewModel.
Form Validation
Unto the last part of this tutorial, validation. In most cases this is done through generic callbacks that can be reused across fields.
The callback checks the field it receives as a parameter and if there is anything wrong with it, the callback returns an error message.
We can use as many callbacks as we need for validation allowing us to display a more accurate error message depending on the validation, alternatively we can perform the entire validation in one callback. There is really no best way as each approach has its own benefits and drawbacks.
In our case, we will check if the field is empty or not as all the information we request on our form is required.
We only have two types of values in our form, a
string
for the title and ISBN and an array ofstring
s for the list of authors. We will handle both cases in one callback for simplicity. If things get complicated, we can define multiple callbacks to handle each type of value in particular. For instance,requiredText
,requiredNumber
,requiredCollection
and so on.Next, we will update our form definition and configure the validator callback for each field.
We only need to include the error message in our presentation, for this we will update the
TextInput
component.Dependent Fields for Validation
The above almost works in our case, the one field where we do not display the error message is the authors and that is because we have a component-level field for the input. Our author field is not directly editable.
This is where we introduce validation triggers for dependent fields.
In some cases, the validity of a field is dependent on the value of another field. As an example, we may have to handle start and end date pairs where the end date must be after the start date. In this case, the validity of the end date is dependent on the value of the start date.
There are other cases where dependencies are formed between fields. The issue is solved the same way for all fields by specifying validation triggers.
A validation trigger can be any ViewModel, they do not have to be other fields necessarily. Whenever one of the triggers notifies that a property has changed, the validation callbacks are evaluated once more to check if the error message for the dependent field has changed. An
undefined
error message means the field is valid.In our case, the
author
field at the component-level is dependent on theauthors
field at the ViewModel level. Even more so, the component-level field will only forwards the error message from its dependent field.This will display the relevant error message from the ViewModel-level field using the
TextInput
component.Conclusions
In this tutorial you have learned to work with React MVVM Forms covering the core concepts as well has configuring triggers for a dependent field.
These are only the core features of forms in
react-model-view-viewmodel
, check the API for more information as the validation options are extensive, including collection-level and collection item validators for more complex scenarios such as ensuring uniqueness of a value in a list.Check the other tutorials in this category for learning more about this library and how to tackle different challenges.
The code can be viewed, edited and run on CodeSandbox here: https://codesandbox.io/s/react-mvvm-forms-example-y3kr45.
There is a CodeSandbox template for getting trying out
react-model-view-viewmodel
as well, https://codesandbox.io/s/react-mvvm-vwsqlv.Beta Was this translation helpful? Give feedback.
All reactions