De formularios y productividad

[English version]Este enlace se abrirá en una ventana nueva

Desde mi punto de vista, una de las tareas más tediosas y aburridas a las que nos tenemos que enfrentar cuando hacemos aplicaciones de frontend es al desarrollo de formularios. Por si fuera poco, es una tarea que nos va a tocar hacer una y mil veces. Pensando en ello, se trata, en realidad, de una tarea repetitiva y, por tanto, como ingenieros de software deberíamos encontrar soluciones que incrementarán nuestra productividad.

Introducción

A lo largo de estos años en Divisa iT hemos tratado de resolver esta situación. Podría remontarme a cuando hacíamos aplicaciones con JSP Este enlace se abrirá en una ventana nuevae implementamos una librería de custom-tags o, más recientemente, con la implementación de un asistente para la creación de formularios ya basado, completamente, en el uso de JavaScript. Claramente todo esto nos ha proporcionado una visión integral del problema y de los mecanismos más adecuados para su resolución.

Cuando comenzamos a utilizar REACT Este enlace se abrirá en una ventana nuevael problema surgió, nuevamente, encima del tablero. Había que desarrollar formularios y, lógicamente, de forma eficaz. En función de la experiencia que teníamos, mi intención era evitar el mayor número posible de tareas repetitivas enfocándonos en una serie de objetivos clave:

  • Accesibilidad: Sin lugar a duda, hoy en día es imprescindible. Buscábamos proporcionar a los desarrolladores una solución lista para su uso que considerara los principios y guías de WCAGEste enlace se abrirá en una ventana nueva.
  • Internacionalización: Somos una compañía española, lo cual nos lleva a tener la internacionalización en nuestro ADN, no sólo debemos soportar idiomas como inglés o francés, sino que, además, tenemos que soportar nuestros distintos idiomas regionales.
  • Aproximación basada en modelos: Tal y como os he contado en otros posts, todos nuestros desarrollos en REACT se basan en el uso de TypeScript Este enlace se abrirá en una ventana nuevay una estricta separación entre el modelo (datos), la recuperación y la presentación. Como no podía ser de otra manera, esta visión tenía que estar también recogida dentro del modelo de formularios.
  • Productividad: Para ello buscaba mecanismos que nos permitieran soportar controles de ayuda vinculados a campos de formulario, determinar cuando un campo es requerido o no, así como dependencias entre distintos campos. Además de estas funcionalidades básicas, por nuestra experiencia sabía que existen otra serie de aspectos a considerar tales como: grupos de repetición o controles de formularios de tipo XOR.
  • Simplicidad: Quería minimizar el número de componentes REACT y, como os decía, intentaba tener una solución lista para su uso para lo cual no sólo necesitábamos esos componentes sino una hoja de estilos completamente funcional y totalmente personalizable basada en SassEste enlace se abrirá en una ventana nueva.

Leyendo todo esto, podríais decirme que la solución que hemos creado iba a ser bastante dogmática (opinionated como dicen los ingleses). Si os soy sincero, cuando construís elementos reutilizables se deben tomar decisiones y eso, supone, que debe funcionar de forma concreta y acotada. A decir verdad, no conozco ninguna librería que no tenga un cierto grado de dogmatismo en sus propuestas.

En las siguientes secciones os voy a intentar contar como hemos dado respuesta a cada uno de los objetivos clave enumerados anteriormente.

Accesibilidad

Es un tema amplio y arduo con numerables aspectos a considerar, por ello sólo voy a citar algunos de los que considero como más importantes. En concreto:

  • Todo campo de formulario debe tener su correspondiente etiqueta (label)
  • El HTML generado debe ser semánticamente válido
  • Debemos proporcionar distintos tipos de ayudas a las personas con discapacidades.

La verdad es que si os poneis a desarrollar un formulario de cero, sin ayuda de ninguna librería, hay tantos aspectos que hay tener en cuenta que lo más probable es que os aburráis y que además cometáis errores.

Todo ello me llevo a tomar una serie de aproximaciones que, creo, nos permiten tener un desarrollo final conforme con las distintas normativas y especificaciones de accesibilidad. Citando sólo alguna de las medidas tomadas:

  • Nos aseguramos de que el código generado sea semánticamente válido. Por ejemplo, generamos ids automáticos, nos aseguramos de que el orden de anidamiento sea correcto y también hacemos foco en el primer control cuando el formulario se muestra en una ventana de diálogo.
  • Para evitar errores, he preferido no incluir un control específico de etiqueta. Las etiquetas se generan de forma automática en todos los casos y no es posible evitar su aparición (salvo mediante CSS). Con el objetivo de hacerlo aun más sencillo, nuestros componentes, como UIInput, generan el texto de la etiqueta a través del atributo name que es obligatorio. Lógicamente, si se quisiera generar una etiqueta personalizada se podría hacer a través del atributo label.
  • Tenemos componentes complejos, como pueden ser un select múltiple o controles de autocompletado. Todos ellos se han diseñado y construido con la accesibilidad en mente.
  • Obviamente, todos los campos requeridos se etiquetan de forma correcta. Basta con marcar el campo como required.

Accesibilidad00Accesibilidad00

Internacionalización

Para traducir etiquetas, ayuda y placeholders utilizamos internamente react-i18nextEste enlace se abrirá en una ventana nueva. Sin que el desarrollador tenga, por tanto, que añadirlo o considerarlo de forma explicita en su proyecto. Todo ello facilita que el desarrollo se oriente a la generación del formulario y en la creación de un esqueleto o fichero base de traducciones.

La traducción real, ya la llevaría a cabo bien un tercero bien un API de traducción o incluso sistemas híbridos.

Aproximación basada en modelos

Tal y como os comenté antes, utilizamos TypeScript acompañado de una clara división de capas. ¿Cómo aplicamos esto desde el punto de vista de los formularios? Yo diría que teniendo en cuenta dos visiones complementarias.

  • Por una parte, nuestros formularios esperan como entrada datos basados en un modelo. Bueno, de hecho, podría ser datos basados en una clase o interfaz TypeScript o incluso una aproximación ligera basada en un JSON de entrada. En cualquier caso nuestros campos usan el modelo para identificar información – la que TypeScript nos visibiliza – como arrays o grupos y que luego es empleada por los distintos controles para rellenar la información. De hecho, todo ello nos permite articular funciones avanzadas como grupos de repetición.
  • Por otra parte, la acción de envío del formulario (submit) es una acción asíncrona basada en promesasEste enlace se abrirá en una ventana nueva. Con ello intentamos dar respuesta a una realidad: los formularios suelen utilizarse para almacenar información y dicho almacenamiento, bien sea una petición fetch Este enlace se abrirá en una ventana nuevao un almacenamiento local en IndexedDBEste enlace se abrirá en una ventana nueva, esta basado en la asincronía. Siendo conscientes de esa realidad, podemos mejorar de forma automática la experiencia de usuario incluyendo ayudas sobre el formulario que digan: "Eh, estoy guardando datos".

Productividad

Sinceramente, creo que hemos hecho un esfuerzo importante para mejorar la productividad tanto de nuestros desarrollados como la experiencia del usuario final. Me voy a centrar en cuatro aspectos fundamentales:

En primer lugar, nuestros componentes pueden personalizarse utilizando un atributo de ayuda. Siempre que el usuario se coloca encima de ese icono (o lo hace de forma accesible), se muestra una ayuda en pantalla.

HintsHints

En segundo lugar, algunas tareas como verificar si los datos de un campo cumplen con un determinado patrón, si el campo es requerido o si incluso existen dependencias con otros campos y como afectan éstas, se pueden controlar de una forma extremadamente sencilla.

RregexpRregexp

Finalmente, cuando desarrollamos formularios en ocasiones tenemos que resolver algunas situaciones complejas.

Por una parte, podríamos tener que mostrar dos campos en pantalla pero que se comporten de tal forma que en cuanto el usuario rellena uno, el otro no está disponible. En realidad, hablamos de una relación XOR entre uno o varios campos. Para resolverlo hemos creado un componente que denominamos UIAnyOf

AnyOfAnyOf

Por otra parte, a veces necesitamos hacer grupos de repetición. Una parafernalia en cuanto a introducir botones de añadir, borrar, subir y bajar. Nuestra solución pasa por utilizar un nuevo componente que denominamos UIRepeatGroup y, por supuesto, que nuestro modelo soporte la estructura.

RepeatGroupRepeatGroup


Simplicidad

Como habréis visto a lo largo del post, he intentado que nuestra solución fuera lo más simple posible. Más allá de eso, he tomado otra serie de decisiones que nos permiten mejorar los tiempos de desarrollo y time-to-market.

  • Tenemos un conjunto de componentes lo más pequeño posible. Para ello hemos expandido el atributo type de los controles de tipo input o select, para soportar otras necesidades como municipio, país o, por ejemplo, selectores basados en una promesa.
  • Usamos una hoja de estilos basada en Sass suficientemente completa como para generar formularios listos para uso minimizando el tiempo necesario para personalizar el look&feel.
  • Como pienso que el rendimiento es crítico, nos hemos preocupado por utilizado por usar useMemo Este enlace se abrirá en una ventana nuevade forma adecuada a lo largo de toda la librería.

Conclusión

Cuando desarrollamos formularios hay múltiples aspectos que hay que tener en cuenta. Usar soluciones como esta proporciona a los clientes mecanismos que permiten mejorar su time-to-market y, además, mejorar la productividad del equipo de desarrollo.

Tenemos mucho que avanzar y muchas mejoras pendientes, si te interesa lo que hacemos no dudes en aplicar a nuestras ofertas de empleoEste enlace se abrirá en una ventana nueva.