Forme și validare în reacția ionică
Publicat: 2022-03-10Ionic Framework este un set de instrumente UI pentru construirea de aplicații mobile multiplatforme folosind HTML, CSS și JavaScript. Lansarea lui Ionic 5 la începutul lui 2020 a venit cu suport oficial pentru React, permițând dezvoltatorilor React să creeze cu ușurință aplicații mobile folosind instrumentele lor preferate. Cu toate acestea, nu există prea mult suport pentru lucrul cu formulare și multe dintre bibliotecile existente disponibile pentru construirea de formulare în ecosistemul React nu se joacă bine cu componentele Ionic Framework.
Veți învăța cum să construiți formulare folosind componentele de intrare a interfeței de utilizare ale lui Ionic React în acest tutorial. Veți învăța, de asemenea, cum să utilizați o bibliotecă pentru a ajuta la detectarea modificărilor de introducere a formularelor și pentru a răspunde la regulile de validare. În cele din urmă, veți învăța să vă faceți formularele accesibile cititorilor de ecran, adăugând text util la atributele ARIA ale intrărilor dumneavoastră.
Componentele formei ionice
Formularele reprezintă o parte importantă a majorității aplicațiilor web și mobile din prezent. Indiferent dacă permiteți accesul la părți restricționate ale aplicației dvs. prin înregistrarea utilizatorului și formularele de conectare sau dacă colectați feedback de la utilizatori, trebuie să construiți un formular la un moment dat în ciclul de viață al aplicației dvs.
Ionic oferă componente prefabricate pentru lucrul cu formulare - unele dintre ele includ IonItem
, IonLabel
, IonInput
, IonCheckbox
și IonRadio
. Putem combina aceste componente pentru a construi forme standard fără a adăuga nici un stil.
De exemplu, următorul cod:
<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>
Ne va oferi un formular de conectare care arată astfel:

Din cutie, componentele de formă ale lui Ionic arată grozav pe iOS sau Android, dar pot fi puțin greu de manevrat dacă lucrați cu React. Ca și în cazul majorității instrumentelor din ecosistemul React, trebuie să decideți cum doriți să vă construiți formularele atunci când vine vorba de funcționalitate și accesibilitate - ambele la fel de importante ca și designul.
Deși există deja atât de mulți ajutoare de formulare React disponibile din care să aleagă, majoritatea nu funcționează cu componentele de formular ale lui Ionic. Bănuiesc că motivul principal pentru aceasta este că evenimentul declanșat atunci când valoarea unui câmp se modifică în Ionic este onIonChange
, în timp ce majoritatea bibliotecilor de formulare existente ascultă onChange
.

React Hook Form: bibliotecă de formulare mici și rapide
Din fericire, nu totul este nenorocire și întuneric. Am întâlnit recent React Hook Form (RHF), o bibliotecă pentru lucrul cu formulare în proiectele React. Oferă suport pentru componente controlate sau necontrolate și validarea intrărilor, iar API-ul se bazează pe cârlige, așa că funcționează numai cu componente funcționale.
Cea mai atrăgătoare caracteristică pentru dezvoltatorii Ionic React - în opinia mea - este componenta wrapper <Controller />
pe care o oferă pentru lucrul cu componente controlate. Componenta are o prop onChangeName
care poate fi folosită pentru a specifica numele evenimentului de modificare pentru orice instanță de componentă pe care i-o treceți. Vă voi arăta cum acest lucru face lucrul cu formulare în Ionic cu adevărat ușor în secțiunile următoare.
Crearea unui formular de înscriere
Să vedem cum ne ajută RHF cu funcționalitatea formularului în timp ce construim un formular de înregistrare în Ionic. Dacă rulați cea mai recentă versiune a Ionic CLI (rulați npm i -g @ionic/cli
pentru a confirma), porniți o nouă aplicație Ionic cu React, rulând următoarea comandă:
ionic start myApp blank --type=react
Am folosit un șablon gol aici. Ar trebui să puteți rescrie formularele existente pentru a utiliza cu ușurință biblioteca React Hook Form, mai ales dacă componentele sunt scrise ca Componente funcționale.
Notă: Ar trebui să eliminați componenta ExploreContainer
și importul acesteia în Home.tsx înainte de a continua cu acest tutorial.
Pentru a începe cu formularul, instalați pachetul React Hook Form rulând următoarea comandă în directorul rădăcină al proiectului:
yarn add react-hook-form
Acest lucru va face ca biblioteca React Hook Form să fie disponibilă în proiectul dvs. Să creăm un câmp de introducere a formularului folosind biblioteca. Deschideți fișierul Home.tsx și înlocuiți conținutul acestuia cu următoarele:
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;
Acest lucru vă oferă un formular cu un singur câmp pentru a colecta o adresă de e-mail. Să defalcăm părțile importante (evidențiate în blocul de cod).
În primul rând, destructuram valoarea de returnare a cârligului useForm()
din RHF. handleSubmit
transmite valorile intrării dvs. către funcția de gestionare pe care o specificați când formularul trece validarea. control
este un obiect care conține metode utilizate pentru înregistrarea componentelor controlate în RHF.
În continuare, avem un bloc de elemente de formular standard, dar spre deosebire de exemplul pentru formularul de conectare, trecem componenta IonInput
la componenta <Controller />
a RHF, înregistrăm evenimentul de modificare setând prop onChangeName
a lui <Controller />
la evenimentul de modificare a lui Ionic nume și setați prop de control
la obiectul de control din invocarea useForm()
.
Acest lucru este bun până acum, dar s-ar putea să te trezești că repeți aproape același cod iar și iar. Ați putea încerca să creați o componentă de Input
reutilizabilă care construiește un câmp de intrare cu proprietăți date.
Creați un fișier în directorul src/components numit Input.tsx și adăugați următorul cod la fișier:
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;
Această componentă primește o prop de name
și elemente opționale de control
, component
și label
și redă un câmp de intrare folosind componentele de formă ionică introduse mai devreme. Acest lucru reduce cantitatea de cod pe care trebuie să o scrieți atunci când creați câmpuri de introducere a formularului. Puteți finaliza restul formularului folosind această componentă. Editați fișierul Home.tsx cu următoarele modificări:

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;
Cu configurarea dvs. de până acum, aveți o serie de câmpuri de intrare ale formularului ( name
este singura proprietate necesară), fiecare câmp redat folosind componenta de Input
de mai devreme. Puteți duce acest lucru și mai departe și aveți datele de câmp într-un fișier JSON, păstrând codul din componentele dvs. cu formulare curate. În acest moment, aplicația dvs. (care rulează la https://localhost:8100 cu comanda ionic serve
) ar trebui să arate astfel:

Ce zici de validarea câmpului?
S-ar putea să fi observat că câmpurile de introducere ale formularului nostru nu au încă nicio logică de validare. Dacă aceasta ar fi o aplicație destinată utilizării în lumea reală, aceasta ar putea duce la multe efecte nedorite, cu excepția cazului în care API-ul dvs. este configurat pentru a valida datele primite. Apropo, API-ul dvs. trebuie să valideze întotdeauna datele primite.
RHF vine cu validare care se aliniază cu standardul HTML pentru validarea formularelor încorporată. Acest lucru funcționează excelent pentru o validare simplă, cum ar fi solicitarea unui câmp sau setarea lungimii minime și maxime ale câmpurilor. Dacă doriți să utilizați o logică complexă de validare, vă recomand să utilizați Yup. În timp ce puteți utiliza orice bibliotecă de validare a schemei de obiecte, RHF acceptă Yup din cutie.
Rulați următoarea comandă pentru a instala biblioteca (și tastările):
yarn add yup @types/yup
Apoi, adăugați acest lucru la importurile componentei dvs.:
import { object, string } from 'yup'; const Home: React.FC = () => { ... }
Apoi, adăugați următorul cod în partea de sus a componentei dvs.:
const Home: React.FC = () => { const validationSchema = object().shape({ email: string().required().email(), fullName: string().required().min(5).max(32), password: string().required().min(8), }); // ... }
Aici, am creat o schemă de obiecte și am adăugat reguli de validare la fiecare proprietate folosind yup
. Numele din obiect trebuie să se potrivească cu numele din etichetele de intrare ale formularului, altfel regulile nu vor fi declanșate.
În cele din urmă, actualizați cârligul useForm()
pentru a utiliza schema pe care am definit-o prin setarea proprietății validationSchema
astfel:
const { control, handleSubmit } = useForm({ validationSchema, });
Acum, când faceți clic pe butonul de handleSubmit
, handler-ul de trimitere nu este invocat și datele din formular nu sunt trimise. Deși asta este exact ceea ce ne-am dorit, se pare că nu există nicio modalitate ca utilizatorul să știe ce se întâmplă. Să remediam acest lucru afișând sugestii de text atunci când un câmp nu este completat corect.
Mai întâi, actualizați componenta de Input
pentru a arăta astfel:
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;
Aici, ne-am actualizat componenta pentru a primi o proprietate suplimentară opțională care este obiectul de eroare de la RHF și afișăm un mesaj de eroare în câmpul de intrare returnat ori de câte ori există o eroare. Un ultim lucru, adăugați obiectul erori la obiectul dvs. destructurat și actualizați componenta din bucla:
const { control, handleSubmit, errors } = useForm({ validationSchema, });
{formFields.map((field, index) => ( <Input {...field} control={control} key={index} errors={errors} /> ))}

Formularele dvs. oferă acum indicii vizuale atunci când un utilizator nu face ceva corect. Da vă permite să schimbați mesajul de eroare. Puteți face acest lucru pasând un șir metodei de validare pe care o utilizați. Pentru e-mail, de exemplu, puteți face următoarele:
{ email: string() .email('Please provide a valid email address') .required('This is a required field'), }
Îmbunătățirea accesibilității
Componentele lui Ionic sunt de obicei învelișuri peste elementul nativ corespunzător, ceea ce înseamnă că acceptă majoritatea - dacă nu toate - atributele existente ale acelui element. Puteți să vă îmbunătățiți câmpurile de introducere și să le faceți mai accesibile pentru utilizatorii cu deficiențe de vedere prin setarea atributelor ARIA cu text relevant.
Pentru a continua cu exemplul nostru de formular de înregistrare, deschideți fișierul Input.tsx și faceți următoarele modificări:
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;
Componenta implicită IonInput
pe care o transmitem Controller
include acum un atribut aria-invalid
pentru a indica dacă câmpul are o eroare și un atribut aria-describedby
pentru a indica mesajul de eroare corespunzător. Mesajul de eroare este acum împachetat cu un span
având un rol ARIA setat la „eroare”. Acum, când câmpul dvs. are o eroare, un cititor de ecran va evidenția acel câmp și va citi mesajul de eroare.
- Veți găsi depozitul GitHub aici.
Concluzie
Felicitări! Ați învățat cum să creați și să validați formulare atunci când construiți aplicații multiplatformă folosind Ionic. De asemenea, ați văzut cât de ușor este să vă faceți câmpurile de introducere accesibile utilizatorilor cu deficiențe de vedere. Sperăm că acest tutorial oferă o platformă solidă pe care o puteți folosi atunci când construiți formulare în aplicațiile dvs. Ionic React. Există și alte componente pentru construirea formularelor (cum ar fi select și radiouri) pe care nu le-am explorat în acest tutorial, dar puteți găsi și citi mai multe despre ele în documentele oficiale.
Referințe
- Ionic Framework Docs
- React Hook Form
- Da, Docs
- Phil Haack despre validarea adreselor de e-mail
- Accesibilitate pe MDN Web Docs