Formas y Validación en Ionic React

Publicado: 2022-03-10
Resumen rápido ↬ Ionic Framework brinda soporte de primera clase para crear aplicaciones rápidas y optimizadas para dispositivos móviles para cualquier plataforma que use React. En este tutorial, aprenderá cómo crear formularios cuando trabaje con Ionic React y cómo hacer que estos formularios sean interactivos agregando reglas de validación con sugerencias de texto útiles.

Ionic Framework es un kit de herramientas de interfaz de usuario para crear aplicaciones móviles multiplataforma utilizando HTML, CSS y JavaScript. El lanzamiento de Ionic 5 a principios de 2020 vino con soporte oficial para React, lo que permitió a los desarrolladores de React crear fácilmente aplicaciones móviles utilizando sus herramientas favoritas. Sin embargo, no hay mucho soporte para trabajar con formularios, y muchas de las bibliotecas existentes disponibles para crear formularios en el ecosistema React no funcionan bien con los componentes de Ionic Framework.

En este tutorial, aprenderá a crear formularios utilizando los componentes de entrada de la interfaz de usuario de Ionic React. También aprenderá a usar una biblioteca para ayudar a detectar cambios de entrada de formulario y responder a las reglas de validación. Finalmente, aprenderá a hacer que sus formularios sean accesibles para los lectores de pantalla agregando texto útil a los atributos ARIA de sus entradas.

Componentes de forma de Ionic

Los formularios son una parte importante de la mayoría de las aplicaciones web y móviles en la actualidad. Ya sea que esté habilitando el acceso a partes restringidas de su aplicación a través de formularios de inicio de sesión y registro de usuarios o recopilando comentarios de sus usuarios, debe, en algún momento del ciclo de vida de su aplicación, crear un formulario.

Ionic proporciona componentes prediseñados para trabajar con formularios, algunos de los cuales incluyen IonItem , IonLabel , IonInput , IonCheckbox e IonRadio . Podemos combinar estos componentes para crear formularios de apariencia estándar sin agregar ningún estilo nosotros mismos.

Por ejemplo, el siguiente código:

 <form className="ion-padding"> <IonItem> <IonLabel position="floating">Username</IonLabel> <IonInput /> </IonItem> <IonItem> <IonLabel position="floating">Password</IonLabel> <IonInput type="password" /> </IonItem> <IonItem lines="none"> <IonLabel>Remember me</IonLabel> <IonCheckbox defaultChecked={true} slot="start" /> </IonItem> <IonButton className="ion-margin-top" type="submit" expand="block"> Login </IonButton> </form>

Nos dará un formulario de inicio de sesión que se ve así:

Formulario de inicio de sesión estándar en iOS (vista previa grande)

Fuera de la caja, los componentes de formulario de Ionic se ven muy bien en iOS o Android, pero pueden ser un poco difíciles de manejar si está trabajando con React. Al igual que con la mayoría de las herramientas en el ecosistema React, debe decidir cómo quiere construir sus formularios en lo que respecta a la funcionalidad y la accesibilidad, ambas tan importantes como el diseño.

Si bien ya hay muchos asistentes de formulario React disponibles para elegir, la mayoría de ellos no funcionan con los componentes de formulario de Ionic. Sospecho que la razón principal de esto es que el evento se activa cuando cambia el valor de un campo en Ionic es onIonChange , mientras que la mayoría de las bibliotecas de formularios existentes escuchan onChange .

Evento de cambio activado cuando cambia el campo (vista previa grande)

React Hook Form: biblioteca de formularios React pequeños y rápidos

Afortunadamente, no todo es pesimismo. Recientemente me encontré con React Hook Form (RHF), una biblioteca para trabajar con formularios en proyectos de React. Brinda soporte para componentes controlados o no controlados y validación de entrada, y la API está basada en ganchos, por lo que solo funciona con componentes funcionales.

La característica más atractiva para los desarrolladores de Ionic React, en mi opinión, es el componente contenedor <Controller /> que proporciona para trabajar con componentes controlados. El componente tiene una prop onChangeName que se puede usar para especificar el nombre del evento de cambio para cualquier instancia de componente que le pase. Le mostraré cómo esto hace que trabajar con formularios en Ionic sea realmente fácil en las siguientes secciones.

¡Más después del salto! Continúe leyendo a continuación ↓

Crear un formulario de registro

Veamos cómo RHF nos ayuda con la funcionalidad del formulario mientras creamos un formulario de registro en Ionic. Si está ejecutando la última versión de Ionic CLI (ejecute npm i -g @ionic/cli para confirmar), inicie una nueva aplicación Ionic con React ejecutando el siguiente comando:

 ionic start myApp blank --type=react

Usé una plantilla en blanco aquí. Debería poder reescribir sus formularios existentes para usar la biblioteca React Hook Form con facilidad, especialmente si sus componentes están escritos como componentes funcionales.

Nota: debe eliminar el componente ExploreContainer y su importación en Home.tsx antes de continuar con este tutorial.

Para comenzar con su formulario, instale el paquete React Hook Form ejecutando el siguiente comando en el directorio raíz de su proyecto:

 yarn add react-hook-form

Esto hará que la biblioteca React Hook Form esté disponible en su proyecto. Vamos a crear un campo de entrada de formulario usando la biblioteca. Abra el archivo Home.tsx y reemplace su contenido con lo siguiente:

 import { IonContent, IonPage, IonText, IonItem, IonLabel, IonInput, IonButton } from "@ionic/react"; import React from "react"; import "./Home.css"; import { Controller, useForm } from 'react-hook-form'; const Home: React.FC = () => { const { control, handleSubmit } = useForm(); const registerUser = (data) => { console.log('creating a new user account with: ', data); } return ( <IonPage> <IonContent className="ion-padding"> <IonText color="muted"> <h2>Create Account</h2> </IonText> <form onSubmit={handleSubmit(registerUser)}> <IonItem> <IonLabel position="floating">Email</IonLabel> <Controller as={<IonInput type="email" />} name="email" control={control} onChangeName="onIonChange" /> </IonItem> <IonButton expand="block" type="submit" className="ion-margin-top"> Register </IonButton> </form> </IonContent> </IonPage> ); }; export default Home;

Esto le da un formulario con un solo campo para recopilar una dirección de correo electrónico. Analicemos las partes importantes (resaltadas en el bloque de código).

Primero, desestructuramos el valor de retorno del gancho useForm() de RHF. handleSubmit pasa los valores de su entrada a la función del controlador que especifica cuando el formulario pasa la validación. control es un objeto que contiene métodos utilizados para registrar componentes controlados en RHF.

A continuación, tenemos un bloque de elemento de formulario estándar, pero a diferencia del ejemplo para el formulario de inicio de sesión, pasamos el componente IonInput al componente <Controller /> de RHF, registramos el evento de cambio configurando el prop onChangeName de <Controller /> al evento de cambio de Ionic nombre, y establezca la propiedad de control en el objeto de control desde la invocación de useForm() .

Esto es bueno hasta ahora, pero es posible que te encuentres repitiendo casi el mismo código una y otra vez. Podría intentar crear un componente de Input reutilizable que cree un campo de entrada con propiedades dadas.

Cree un archivo en el directorio src/components llamado Input.tsx y agregue el siguiente código al archivo:

 import React, { FC } from "react"; import { IonItem, IonLabel, IonInput } from "@ionic/react"; import { Controller, Control } from "react-hook-form"; export interface InputProps { name: string; control?: Control; label?: string; component?: JSX.Element; } const Input: FC<InputProps> = ({ name, control, component, label, }) => { return ( <> <IonItem> {label && ( <IonLabel position="floating">{label}</IonLabel> )} <Controller as={component ?? <IonInput />} name={name} control={control} onChangeName="onIonChange" /> </IonItem> </> ); }; export default Input;

Este componente recibe un accesorio de name y accesorios opcionales de control , component y label y representa un campo de entrada utilizando los componentes de formulario Ionic presentados anteriormente. Esto reduce la cantidad de código que tiene que escribir al crear campos de entrada de formulario. Puede terminar el resto de su formulario usando este componente. Edite el archivo Home.tsx con los siguientes cambios:

 import { IonContent, IonPage, IonText, IonInput, IonButton, IonCheckbox, IonItem, IonLabel } from "@ionic/react"; import React from "react"; import "./Home.css"; import { useForm } from "react-hook-form"; import Input, { InputProps } from "../components/Input"; const Home: React.FC = () => { const { control, handleSubmit } = useForm(); const formFields: InputProps[] = [ { name: "email", component: <IonInput type="email" />, label: "Email", }, { name: "fullName", label: "Full Name", }, { name: "password", component: <IonInput type="password" clearOnEdit={false} />, label: "Password", }, ]; const registerUser = (data) => { console.log("creating a new user account with: ", data); }; return ( <IonPage> <IonContent> <div className="ion-padding"> <IonText color="muted"> <h2>Create Account</h2> </IonText> <form onSubmit={handleSubmit(registerUser)}> {formFields.map((field, index) => ( <Input {...field} control={control} key={index} /> ))} <IonItem> <IonLabel>I agree to the terms of service</IonLabel> <IonCheckbox slot="start" /> </IonItem> <IonButton expand="block" type="submit" className="ion-margin-top"> Register </IonButton> </form> </div> </IonContent> </IonPage> ); }; export default Home;

Con su configuración hasta el momento, tiene una matriz de los campos de entrada de su formulario (el name es la única propiedad requerida), con cada campo representado usando el componente Input de antes. Puede llevar esto aún más lejos y tener sus datos de campo en un archivo JSON, manteniendo limpio el código dentro de sus componentes con formularios. En este punto, su aplicación (que se ejecuta en https://localhost:8100 con el comando de ionic serve ) debería verse así:

Página del formulario de registro (iOS) (vista previa grande)

¿Qué hay de la validación de campo?

Es posible que haya notado que los campos de entrada de nuestro formulario aún no tienen ninguna lógica de validación. Si se tratara de una aplicación destinada al uso en el mundo real, eso podría provocar muchos efectos no deseados a menos que su API esté configurada para validar los datos entrantes. Por cierto, su API siempre debe validar los datos entrantes.

RHF viene con una validación que se alinea con el estándar HTML para la validación de formularios incorporada. Esto funciona muy bien para una validación simple, como hacer que un campo sea obligatorio o establecer longitudes de campo mínimas y máximas. Si desea utilizar una lógica de validación compleja, le recomendaría utilizar Yup. Si bien puede usar cualquier biblioteca de validación de esquemas de objetos, RHF admite Yup desde el primer momento.

Ejecute el siguiente comando para instalar la biblioteca (y los tipos):

 yarn add yup @types/yup

A continuación, agregue esto a las importaciones de su componente:

 import { object, string } from 'yup'; const Home: React.FC = () => { ... }

Luego, agregue el siguiente código en la parte superior de su componente:

 const Home: React.FC = () => { const validationSchema = object().shape({ email: string().required().email(), fullName: string().required().min(5).max(32), password: string().required().min(8), }); // ... }

Aquí, creamos un esquema de objeto y agregamos reglas de validación a cada propiedad usando yup . Los nombres en el objeto deben coincidir con los nombres en las etiquetas de entrada de su formulario; de lo contrario, sus reglas no se activarán.

Finalmente, actualice su useForm() para usar el esquema que hemos definido configurando la propiedad validationSchema de esta manera:

 const { control, handleSubmit } = useForm({ validationSchema, });

Ahora, cuando hace clic en el botón Enviar, el controlador handleSubmit no se invoca y los datos del formulario no se envían. Si bien esto es exactamente lo que queríamos, parece que no hay forma de que el usuario sepa lo que está sucediendo. Arreglemos esto mostrando sugerencias de texto cuando un campo no se llene correctamente.

Primero, actualice el componente de Input para que tenga el siguiente aspecto:

 import React, { FC } from "react"; import { IonItem, IonLabel, IonInput, IonText } from "@ionic/react"; import { Controller, Control, NestDataObject, FieldError } from "react-hook-form"; export interface InputProps { name: string; control?: Control; label?: string; component?: JSX.Element; errors?: NestDataObject<Record<string, any>, FieldError>; } const Input: FC<InputProps> = ({ name, control, component, label, errors, }) => { return ( <> <IonItem> {label && <IonLabel position="floating">{label}</IonLabel>} <Controller as={component ?? <IonInput />} name={name} control={control} onChangeName="onIonChange" /> </IonItem> {errors && errors[name] && ( <IonText color="danger" className="ion-padding-start"> <small>{errors[name].message}</small> </IonText> )} </> ); }; export default Input;

Aquí, hemos actualizado nuestro componente para recibir una propiedad opcional adicional que es el objeto de error de RHF, y mostramos un mensaje de error en el campo de entrada devuelto cada vez que hay un error. Una última cosa, agregue el objeto de errores a su objeto desestructurado y actualice el componente en su bucle:

 const { control, handleSubmit, errors } = useForm({ validationSchema, });
 {formFields.map((field, index) => ( <Input {...field} control={control} key={index} errors={errors} /> ))}
Formulario de registro con mensajes de error (iOS) (vista previa grande)

Sus formularios ahora brindan señales visuales cuando un usuario no está haciendo algo bien. Sí, le permite cambiar el mensaje de error. Puede hacerlo pasando una cadena al método de validación que está utilizando. Para el correo electrónico, por ejemplo, puede hacer lo siguiente:

 { email: string() .email('Please provide a valid email address') .required('This is a required field'), }

Mejorar la accesibilidad

Los componentes de Ionic suelen ser envoltorios sobre el elemento nativo correspondiente, lo que significa que aceptan la mayoría, si no todos, de los atributos existentes de ese elemento. Puede mejorar sus campos de entrada y hacerlos más accesibles para los usuarios con discapacidades visuales configurando atributos ARIA con texto relevante.

Para continuar con nuestro formulario de registro de ejemplo, abra el archivo Input.tsx y realice los siguientes cambios:

 import React, { FC } from "react"; import { IonItem, IonLabel, IonInput, IonText } from "@ionic/react"; import { Controller, Control, NestDataObject, FieldError } from "react-hook-form"; export interface InputProps { name: string; control?: Control; label?: string; component?: JSX.Element; errors?: NestDataObject<Record<string, any>, FieldError>; } const Input: FC<InputProps> = ({ name, control, component, label, errors, }) => { return ( <> <IonItem> {label && <IonLabel position="floating">{label}</IonLabel>} <Controller as={ component ?? ( <IonInput aria-invalid={errors && errors[name] ? "true" : "false"} aria-describedby={`${name}Error`} /> ) } name={name} control={control} onChangeName="onIonChange" /> </IonItem> {errors && errors[name] && ( <IonText color="danger" className="ion-padding-start"> <small> <span role="alert" id={`${name}Error`}> {errors[name].message} </span> </small> </IonText> )} </> ); }; export default Input;

El componente IonInput predeterminado que estamos pasando a Controller ahora incluye un atributo aria-invalid para indicar si el campo tiene un error y un atributo aria-describedby para señalar el mensaje de error correspondiente. El mensaje de error ahora está envuelto con un span que tiene un rol ARIA establecido en "error". Ahora, cuando su campo tenga un error, un lector de pantalla resaltará ese campo y leerá el mensaje de error.

  • Encontrarás el repositorio de GitHub aquí.

Conclusión

¡Felicidades! Ha aprendido a crear y validar formularios al crear aplicaciones multiplataforma con Ionic. También ha visto lo fácil que es hacer que sus campos de entrada sean accesibles para usuarios con discapacidad visual. Con suerte, este tutorial proporciona una plataforma sólida que puede usar al crear formularios en sus aplicaciones Ionic React. Hay otros componentes para crear formularios (como selección y radios) que no exploramos en este tutorial, pero puede encontrarlos y leer más sobre ellos en los documentos oficiales.

Referencias

  • Documentos de Ionic Framework
  • Forma de gancho de reacción
  • Sí, documentos
  • Phil Haack sobre la validación de direcciones de correo electrónico
  • Accesibilidad en MDN Web Docs