About forms and productivity

[Versión en castellano]This link opens in a popup window

From my point of view, creating forms is, unarguably, one of the most boring and tedious programming tasks to perform when developing a frontend application. Worst of all, you are going to do it over and over. Therefore, we could consider it as a repetitive task and, hence, we, as software engineers, should find solutions that make us more productive.

Introduction

At Divisa iT we are very aware of this problem, and we have implemented different solutions over time. From custom taglibs, used in long-forgotten JSPThis link opens in a popup window applications, to form creation wizards. This experience has provided us with a deep insight into the different form-based-application use cases.

When we started using REACTThis link opens in a popup window this problem raised again. We needed to develop forms, and we needed to develop them swiftly. My aim was avoiding as many monotonous tasks as possible, while focusing on a series of key goals such as:

  • Accessibility: Undoubtedly, it is a must. Our solution should give our developers a ready-to-use solution that should consider WCAGThis link opens in a popup window principles and guidelines.
  • Internationalization: We are a Spanish company, and, as you should know, in Spain there are different regional languages, so internationalization is essential for us. Moreover, a lot of our clients need to publish information and forms in foreign languages such as English or French.
  • Model-based-approach: As you know from my previous posts, not only are we using TypeScriptThis link opens in a popup window in our developments but also a strict separation of concerns between model, data acquisition and presentation. Consequently, I wanted this approach to be used in forms as well.
  • Productivity: That means, that our forms should give mechanisms to simplify tasks like supporting input fields hints, controlling required ones, and detecting dependency errors. In addition, I also wanted to solve some complex form tasks such as repeating groups or XOR input fields.
  • Simplicity: I tried to use as few custom REACT components as possible and to have a presentation ready solution using a fully-defined and totally customizable SassThis link opens in a popup window stylesheet.

Obviously, you could argue that we have created an opinionated solution. As a matter of fact, when you build something reusable you must take decisions. I mean to say, that it has work in a certain and well-defined way. In fact, I don’t know any library which isn’t opinionated to some extent.

In the following sections I’ll try to show you how we have implemented solutions to the different objectives described below.

Accessibility

Although there are several issues to consider, I’ll focus only on the main ones, such as:

  • There cannot be a control (an input field) without a label.
  • HTML must be semantically valid.
  • We must provide hints and help to disabled people.

Developing a form from scratch, and taking into account all these points is, in my opinion, boring and prone to errors.

That’s why, I have taken some decisions which drive to a more compliant final development. For example:

  • We ensure that generated code is semantically valid. For example, we generate automatic id, we enforce tag nesting in the proper way, and we make focus on the first control when the form is shown in a dialog.
  • Our custom components don’t distinguish between labels and controls. For example, our component UInput expects a name which is used – thanks to internationalization – to create the label. On the other hand, if you don’t want to use the default solution the component also allows a label attribute.
  • Complex components as multiple-select or even auto-complete ones are built with accessibility in mind.
  • All required fields are labelled properly and automatically. You only need to set that control as required .

Accesibilidad00Accesibilidad00

Internationalization

We are using, under the hood, react-i18nextThis link opens in a popup window to translate all labels, hints, and placeholders. As a result, our developers should only focus on the form development and the creation of a translation file skeleton. Actual translation could be provided by a third-party or even a translation API.

Model-based-approach

As I told you previously, we use TypeScript and a strict separation of concerns. From forms point of view this introduces two interesting perspectives.

  • On the one hand, our form expects an actual model to work with. As a matter of fact, it could be a TypeScript-class-based object or even a less rigid JSON one. Internally, our controls use the model and manipulate the data to inject values considering array-based and group-based ones. Furthermore, model definition is used to populate new instances in repetition groups.
  • On the other hand, the submit action is a promiseThis link opens in a popup window-based one. In other words, when the end-user submits the form, we are expecting that it’ll trigger an action which will be probably store information in a promised-based target as, for example, a fetchThis link opens in a popup window call or an IndexedDBThis link opens in a popup window storage. That allows us to improve user-experience by providing a default Saving component which will be displayed over the form. Obviously, this behaviour could be overridden.

Productivity

As I see it, we made an important effort to improve both the productivity of our developers and the end-user experience. Main results include:

In the first place, our custom components could be customized using a help attribute. Whenever the user hovers the help icon, help text is shown on screen considering all accessibility issues.

HintsHints

In the second place, some tasks such as checking the value of an input field, verifying if it’s required or even if there is a dependency between two or more controls is achieved in an extremely simple way.

RregexpRregexp

Last, but not least, when we develop forms, we are sometimes in the need to solve some complex issues.

On the one hand, imagine a situation in which you are displaying two fields, but you want the user to enter data in only one of them. In fact, those are XOR based fields, one or the other, but not both. We have solved this problem introducing a custom component named UIAnyOf

AnyOfAnyOf

On the other hand, sometimes we also need to develop repeating groups, you need to support arrays, to introduce add and delete buttons and even move-up and move-down ones. Our solution involves using a custom component named UIRepeatGroup, of course your model must support this structure.

RepeatGroupRepeatGroup

Simplicity

As you could notice I have tried to design our solution as simple as possible. Moreover, I have also taken other decisions which help us to improve our development times.

  • We have the minimum set of components, that means that we have expanded input and select type attribute to support other needs as, for example, municipality, country, townhall, promised-based select, etc.
  • We are using a fully customizable Sass stylesheet. In my opinion, we are providing a ready-to-use solution which minimises time-to-market.
  • We think that performance is critical. Thus, we have implemented thoroughly useMemo across our library.

In summary

When developing forms too many issues are involved. Solutions as this one provide mechanisms to improve both client time-to-market and developers productivity.