Forme E Validazione In Reazione Ionica
Pubblicato: 2022-03-10Ionic Framework è un UI Toolkit per la creazione di applicazioni mobili multipiattaforma utilizzando HTML, CSS e JavaScript. Il rilascio di Ionic 5 all'inizio del 2020 è arrivato con il supporto ufficiale per React, consentendo agli sviluppatori React di creare facilmente applicazioni mobili utilizzando i loro strumenti preferiti. Non c'è molto supporto per lavorare con i moduli, tuttavia, e molte delle librerie esistenti disponibili per la creazione di moduli nell'ecosistema React non funzionano bene con i componenti di Ionic Framework.
Imparerai come creare moduli utilizzando i componenti di input dell'interfaccia utente di Ionic React in questo tutorial. Imparerai anche come utilizzare una libreria per rilevare le modifiche all'input del modulo e rispondere alle regole di convalida. Infine, imparerai a rendere i tuoi moduli accessibili agli screen reader aggiungendo testo utile agli attributi ARIA dei tuoi input.
Componenti della forma di Ionic
I moduli sono oggi una parte importante della maggior parte delle applicazioni Web e mobili. Sia che tu stia abilitando l'accesso a parti riservate della tua applicazione tramite la registrazione utente e moduli di accesso o raccogliendo feedback dai tuoi utenti, devi, a un certo punto del ciclo di vita della tua applicazione, creare un modulo.
Ionic fornisce componenti predefiniti per lavorare con i moduli, alcuni dei quali includono IonItem
, IonLabel
, IonInput
, IonCheckbox
e IonRadio
. Possiamo combinare questi componenti per costruire forme dall'aspetto standard senza aggiungere alcuno stile noi stessi.
Ad esempio, il seguente codice:
<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>
Ci darà un modulo di accesso che assomiglia a questo:
Di default, i componenti del modulo di Ionic hanno un bell'aspetto su iOS o Android, ma possono essere un po' ingombranti se stai lavorando con React. Come con la maggior parte degli strumenti nell'ecosistema React, devi decidere come vuoi costruire i tuoi moduli quando si tratta di funzionalità e accessibilità, entrambi altrettanto importanti quanto il design.
Sebbene ci siano già così tanti form helper React disponibili tra cui scegliere, la maggior parte di essi non funziona con i componenti del modulo di Ionic. Sospetto che la ragione principale di ciò sia che l'evento generato quando un valore di campo cambia in Ionic è onIonChange
, mentre la maggior parte delle librerie di moduli esistenti ascolta onChange
.
React Hook Form: Libreria di moduli React piccola e veloce
Per fortuna, non è tutto destino e oscurità. Di recente mi sono imbattuto in React Hook Form (RHF), una libreria per lavorare con i moduli nei progetti React. Fornisce supporto per componenti controllati o non controllati e convalida dell'input e l'API è basata su hook, quindi funziona solo con componenti funzionali.
La caratteristica più interessante per gli sviluppatori di Ionic React, secondo me, è il componente wrapper <Controller />
che fornisce per lavorare con i componenti controllati. Il componente ha un prop onChangeName
che può essere utilizzato per specificare il nome dell'evento di modifica per qualsiasi istanza del componente che gli passi. Ti mostrerò come questo renda davvero facile lavorare con i moduli in Ionic nelle sezioni seguenti.
Creazione di un modulo di iscrizione
Vediamo come RHF ci aiuta con la funzionalità dei moduli mentre creiamo un modulo di registrazione in Ionic. Se stai eseguendo l'ultima versione di Ionic CLI (esegui npm i -g @ionic/cli
per confermare), avvia una nuova app Ionic con React eseguendo il comando seguente:
ionic start myApp blank --type=react
Ho usato un modello vuoto qui. Dovresti essere in grado di riscrivere i tuoi moduli esistenti per utilizzare facilmente la libreria React Hook Form, specialmente se i tuoi componenti sono scritti come componenti funzionali.
Nota: è necessario rimuovere il componente ExploreContainer
e la sua importazione in Home.tsx prima di procedere con questo tutorial.
Per iniziare con il tuo modulo, installa il pacchetto React Hook Form eseguendo il seguente comando nella directory principale del tuo progetto:
yarn add react-hook-form
Ciò renderà disponibile la libreria React Hook Form nel tuo progetto. Creiamo un campo di input del modulo usando la libreria. Apri il file Home.tsx e sostituisci il suo contenuto con il seguente:
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;
Questo ti dà un modulo con un singolo campo per raccogliere un indirizzo email. Analizziamo le parti importanti (evidenziate nel blocco di codice).
Per prima cosa, destrutturiamo il valore di ritorno useForm()
da RHF. handleSubmit
passa i valori dell'input alla funzione del gestore specificata quando il modulo supera la convalida. control
è un oggetto contenente i metodi utilizzati per registrare i componenti controllati in RHF.
Successivamente, abbiamo un blocco di elementi del modulo standard, ma a differenza dell'esempio per il modulo di accesso, passiamo il componente IonInput
al componente <Controller />
di RHF, registriamo l'evento di modifica impostando il prop onChangeName
di <Controller />
sull'evento di modifica di Ionic name e imposta il prop di control
sull'oggetto di controllo invocando useForm()
.
Questo è buono finora, ma potresti ritrovarti a ripetere quasi lo stesso codice più e più volte. Potresti provare a creare un componente di Input
riutilizzabile che costruisca un campo di input con determinate proprietà.
Creare un file nella directory src/components denominato Input.tsx e aggiungere il codice seguente al file:
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;
Questo componente riceve un name
prop e control
opzionali, component
ed label
props e esegue il rendering di un campo di input utilizzando i componenti della forma ionica introdotti in precedenza. Ciò riduce la quantità di codice da scrivere durante la creazione dei campi di input del modulo. Puoi completare il resto del modulo utilizzando questo componente. Modifica il file Home.tsx con le seguenti modifiche:
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 la tua configurazione finora, hai una matrice dei campi di input del tuo modulo (il name
è l'unica proprietà richiesta), con ogni campo visualizzato utilizzando il componente Input
di prima. Puoi andare ancora oltre e avere i dati del tuo campo in un file JSON, mantenendo pulito il codice all'interno dei tuoi componenti con i moduli. A questo punto, la tua app (in esecuzione su https://localhost:8100 con il comando ionic serve
) dovrebbe apparire così:
Che ne dici della convalida del campo?
Potresti aver notato che i campi di input del nostro modulo non hanno ancora alcuna logica di convalida. Se si trattasse di un'app destinata all'uso nel mondo reale, ciò potrebbe portare a molti effetti indesiderati a meno che l'API non sia configurata per convalidare i dati in entrata. A proposito, la tua API deve sempre convalidare i dati in entrata.
RHF viene fornito con la convalida che si allinea allo standard HTML per la convalida dei moduli incorporata. Funziona alla grande per una semplice convalida come la creazione di un campo obbligatorio o l'impostazione di lunghezze di campo minime e massime. Se desideri utilizzare una logica di convalida complessa, ti consiglio di utilizzare Yup. Sebbene sia possibile utilizzare qualsiasi libreria di convalida dello schema di oggetti, RHF supporta Yup pronto all'uso.
Esegui il comando seguente per installare la libreria (e le digitazioni):
yarn add yup @types/yup
Quindi, aggiungi questo alle importazioni del tuo componente:
import { object, string } from 'yup'; const Home: React.FC = () => { ... }
Quindi, aggiungi il seguente codice nella parte superiore del tuo 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), }); // ... }
Qui, abbiamo creato uno schema di oggetti e aggiunto regole di convalida a ciascuna proprietà utilizzando yup
. I nomi nell'oggetto devono corrispondere ai nomi nei tag di input del modulo, altrimenti le regole non verranno attivate.
Infine, aggiorna il tuo useForm()
per utilizzare lo schema che abbiamo definito impostando la proprietà validationSchema
in questo modo:
const { control, handleSubmit } = useForm({ validationSchema, });
Ora, quando fai clic sul pulsante di invio, il gestore handleSubmit
non viene richiamato e i dati del modulo non vengono inviati. Anche se questo è esattamente ciò che volevamo, sembra che non ci sia modo per l'utente di sapere cosa sta succedendo. Risolviamo questo problema mostrando suggerimenti di testo quando un campo non è compilato correttamente.
Innanzitutto, aggiorna il componente Input
in modo che assomigli al seguente:
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;
Qui, abbiamo aggiornato il nostro componente per ricevere una proprietà opzionale aggiuntiva che è l'oggetto di errore da RHF e visualizziamo un messaggio di errore nel campo di input restituito ogni volta che si verifica un errore. Un'ultima cosa, aggiungi l'oggetto errori al tuo oggetto destrutturato e aggiorna il componente nel tuo ciclo:
const { control, handleSubmit, errors } = useForm({ validationSchema, });
{formFields.map((field, index) => ( <Input {...field} control={control} key={index} errors={errors} /> ))}
I tuoi moduli ora forniscono segnali visivi quando un utente non sta facendo qualcosa di giusto. Yup ti consente di modificare il messaggio di errore. Puoi farlo passando una stringa al metodo di convalida che stai utilizzando. Per l'e-mail, ad esempio, puoi fare quanto segue:
{ email: string() .email('Please provide a valid email address') .required('This is a required field'), }
Migliorare l'accessibilità
I componenti di Ionic sono solitamente wrapper sull'elemento nativo corrispondente, il che significa che accettano la maggior parte, se non tutti, degli attributi esistenti di quell'elemento. Puoi migliorare i tuoi campi di input e renderli più accessibili agli utenti ipovedenti impostando gli attributi ARIA con testo pertinente.
Per continuare con il nostro modulo di registrazione di esempio, apri il file Input.tsx e apporta le seguenti modifiche:
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;
Il componente IonInput
predefinito che stiamo passando a Controller
ora include un attributo aria-invalid
per indicare se il campo ha un errore e un attributo aria-describedby
per puntare al messaggio di errore corrispondente. Il messaggio di errore è ora racchiuso in un span
con un ruolo ARIA impostato su "errore". Ora, quando il tuo campo presenta un errore, un'utilità per la lettura dello schermo evidenzierà quel campo e leggerà il messaggio di errore.
- Troverai il repository GitHub qui.
Conclusione
Congratulazioni! Hai imparato come creare e convalidare moduli durante la creazione di app multipiattaforma utilizzando Ionic. Hai anche visto quanto sia facile rendere i campi di input accessibili agli utenti con disabilità visive. Si spera che questo tutorial fornisca una solida piattaforma che puoi utilizzare durante la creazione di moduli nelle tue app Ionic React. Ci sono altri componenti per la creazione di moduli (come select e radio) che non abbiamo esplorato in questo tutorial, ma puoi trovare e leggere di più su di essi nei documenti ufficiali.
Riferimenti
- Documenti di struttura ionica
- Reagire a forma di gancio
- Sì, documenti
- Phil Haack sulla convalida degli indirizzi e-mail
- Accessibilità su MDN Web Docs