Formy i walidacja w reakcji jonowej

Opublikowany: 2022-03-10
Szybkie podsumowanie ↬ Ionic Framework zapewnia pierwszorzędne wsparcie dla tworzenia szybkich i zoptymalizowanych pod kątem urządzeń mobilnych aplikacji dla dowolnej platformy korzystającej z React. W tym samouczku dowiesz się, jak tworzyć formularze podczas pracy z Ionic React i jak uczynić te formularze interaktywnymi, dodając reguły walidacji z pomocnymi wskazówkami tekstowymi.

Ionic Framework to zestaw narzędzi interfejsu użytkownika do tworzenia wieloplatformowych aplikacji mobilnych przy użyciu HTML, CSS i JavaScript. Wydanie Ionic 5 na początku 2020 roku przyniosło oficjalne wsparcie dla Reacta, umożliwiając programistom Reacta łatwe tworzenie aplikacji mobilnych przy użyciu ich ulubionych narzędzi. Nie ma jednak dużego wsparcia dla pracy z formularzami, a wiele istniejących bibliotek dostępnych do budowania formularzy w ekosystemie React nie współpracuje dobrze z komponentami Ionic Framework.

W tym samouczku dowiesz się, jak tworzyć formularze przy użyciu komponentów wejściowych interfejsu użytkownika Ionic React. Dowiesz się również, jak korzystać z biblioteki, aby pomóc w wykrywaniu zmian wprowadzanych w formularzu i reagowaniu na reguły walidacji. Na koniec dowiesz się, jak udostępniać formularze czytnikom ekranu, dodając pomocny tekst do atrybutów ARIA danych wejściowych.

Komponenty postaci jonowej

Formularze są obecnie ważną częścią większości aplikacji internetowych i mobilnych. Niezależnie od tego, czy umożliwiasz dostęp do zastrzeżonych części aplikacji poprzez rejestrację użytkownika i formularze logowania, czy zbieranie opinii od użytkowników, musisz — w pewnym momencie cyklu życia aplikacji — zbudować formularz.

Ionic dostarcza wstępnie zbudowane komponenty do pracy z formularzami — niektóre z nich to IonItem , IonLabel , IonInput , IonCheckbox i IonRadio . Możemy łączyć te elementy, aby budować standardowe formy bez dodawania jakiejkolwiek stylizacji.

Na przykład następujący kod:

 <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>

Otrzymamy formularz logowania, który wygląda tak:

Standardowy formularz logowania na iOS (duży podgląd)

Po wyjęciu z pudełka komponenty formy Ionic wyglądają świetnie na iOS lub Androidzie, ale mogą być nieco nieporęczne, jeśli pracujesz z Reactem. Podobnie jak w przypadku większości narzędzi w ekosystemie React, musisz zdecydować, jak chcesz budować swoje formularze, jeśli chodzi o funkcjonalność i dostępność — obie równie ważne jak projektowanie.

Chociaż jest już tak wiele pomocników formularzy React dostępnych do wyboru, większość z nich nie działa z komponentami formularzy Ionic. Podejrzewam, że głównym powodem jest to, że zdarzenie wywoływane, gdy zmienia się wartość pola w Ionic, jest onIonChange , podczas gdy większość istniejących bibliotek formularzy nasłuchuje onChange .

Zdarzenie zmiany wyzwalane, gdy zmienia się pole (duży podgląd)

React Hook Form: Biblioteka małych i szybkich formularzy React

Na szczęście to nie tylko zguba i przygnębienie. Niedawno natknąłem się na React Hook Form (RHF), bibliotekę do pracy z formularzami w projektach React. Zapewnia obsługę kontrolowanych lub niekontrolowanych komponentów i walidację danych wejściowych, a API jest oparte na hakach, więc działa tylko z komponentami funkcjonalnymi.

Najbardziej atrakcyjną funkcją dla programistów Ionic React — moim zdaniem — jest opakowujący komponent <Controller /> , który zapewnia do pracy z kontrolowanymi komponentami. Komponent ma onChangeName , której można użyć do określenia nazwy zdarzenia zmiany dla dowolnej przekazanej do niego instancji komponentu. W kolejnych sekcjach pokażę ci, jak to sprawia, że ​​praca z formularzami w Ionic jest naprawdę łatwa.

Więcej po skoku! Kontynuuj czytanie poniżej ↓

Tworzenie formularza rejestracyjnego

Zobaczmy, jak RHF pomaga nam w funkcjonalności formularza, gdy budujemy formularz rejestracyjny w Ionic. Jeśli używasz najnowszej wersji Ionic CLI (uruchom npm i -g @ionic/cli , aby potwierdzić), uruchom nową aplikację Ionic za pomocą React, uruchamiając następujące polecenie:

 ionic start myApp blank --type=react

Użyłem tutaj pustego szablonu. Powinieneś być w stanie przepisać istniejące formularze, aby z łatwością korzystać z biblioteki React Hook Form, zwłaszcza jeśli twoje komponenty są napisane jako komponenty funkcjonalne.

Uwaga: Przed kontynuowaniem tego samouczka należy usunąć składnik ExploreContainer i jego import do Home.tsx.

Aby rozpocząć pracę z formularzem, zainstaluj pakiet React Hook Form, uruchamiając następujące polecenie w katalogu głównym projektu:

 yarn add react-hook-form

Dzięki temu biblioteka React Hook Form będzie dostępna w Twoim projekcie. Stwórzmy pole wejściowe formularza za pomocą biblioteki. Otwórz plik Home.tsx i zastąp jego zawartość następującym:

 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;

Daje to formularz z jednym polem do pobrania adresu e-mail. Rozbijmy ważne części (podświetlone w bloku kodu).

Najpierw destrukturyzujemy wartość zwracaną przez hook useForm() z RHF. handleSubmit przekazuje wartości danych wejściowych do funkcji obsługi, którą określisz, gdy formularz przejdzie walidację. control to obiekt zawierający metody służące do rejestracji kontrolowanych elementów w RHF.

Dalej mamy standardowy blok pozycji formularza, ale w przeciwieństwie do przykładu dla formularza logowania, przekazujemy komponent IonInput do komponentu <Controller /> RHF, rejestrujemy zdarzenie change ustawiając właściwość onChangeName <Controller / <Controller /> na zdarzenie change w Ionic name i ustaw właściwość control na obiekt kontrolny z wywołania useForm() .

Jak dotąd jest to dobre, ale może się okazać, że będziesz powtarzać prawie ten sam kod w kółko. Możesz spróbować stworzyć składnik Input wielokrotnego użytku, który buduje pole wejściowe z określonymi właściwościami.

Utwórz plik w katalogu src/components o nazwie Input.tsx i dodaj do niego następujący kod:

 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;

Komponent ten otrzymuje atrybut name prop i opcjonalne właściwości control , component i label oraz renderuje pole wejściowe za pomocą wprowadzonych wcześniej składników postaci jonowej. Zmniejsza to ilość kodu, który musisz napisać podczas tworzenia pól wejściowych formularza. Za pomocą tego komponentu możesz dokończyć resztę formularza. Edytuj plik Home.tsx z następującymi zmianami:

 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;

W dotychczasowej konfiguracji masz tablicę pól wejściowych formularza ( name jest jedyną wymaganą właściwością), przy czym każde pole jest renderowane przy użyciu komponentu Input z wcześniej. Możesz pójść jeszcze dalej i mieć dane pola w pliku JSON, zachowując kod w swoich komponentach za pomocą formularzy. W tym momencie Twoja aplikacja (działająca pod adresem https://localhost:8100 za pomocą polecenia ionic serve ) powinna wyglądać tak:

Strona formularza rejestracyjnego (iOS) (duży podgląd)

A co z walidacją w terenie?

Być może zauważyłeś, że pola wejściowe naszego formularza nie mają jeszcze żadnej logiki walidacji. Gdyby była to aplikacja przeznaczona do użytku w świecie rzeczywistym, mogłoby to prowadzić do wielu niepożądanych efektów, chyba że interfejs API jest skonfigurowany do weryfikowania przychodzących danych. Przy okazji, Twój interfejs API musi zawsze weryfikować przychodzące dane.

RHF jest wyposażony w walidację, która jest zgodna ze standardem HTML dla wbudowanej walidacji formularzy. Działa to doskonale w przypadku prostej walidacji, takiej jak tworzenie wymaganego pola lub ustawianie minimalnej i maksymalnej długości pola. Jeśli chcesz używać złożonej logiki walidacji, polecam użycie Yup. Chociaż możesz użyć dowolnej biblioteki walidacji schematu obiektów, RHF obsługuje Yup po wyjęciu z pudełka.

Uruchom następujące polecenie, aby zainstalować bibliotekę (i wpisując):

 yarn add yup @types/yup

Następnie dodaj to do importów Twojego komponentu:

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

Następnie dodaj następujący kod w górnej części swojego komponentu:

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

Tutaj stworzyliśmy schemat obiektu i dodaliśmy reguły walidacji do każdej właściwości za pomocą yup . Nazwy w obiekcie muszą być zgodne z nazwami w tagach wejściowych formularza, w przeciwnym razie reguły nie zostaną uruchomione.

Na koniec zaktualizuj swój useForm() tak, aby używał zdefiniowanego przez nas schematu, ustawiając właściwość validationSchema w następujący sposób:

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

Teraz, gdy klikniesz przycisk przesyłania, procedura obsługi handleSubmit nie zostanie wywołana, a dane formularza nie zostaną przesłane. Chociaż to jest dokładnie to, czego chcieliśmy, wygląda na to, że użytkownik nie może wiedzieć, co się dzieje. Naprawmy to, wyświetlając wskazówki tekstowe, gdy pole nie jest poprawnie wypełnione.

Najpierw zaktualizuj komponent Input , aby wyglądał następująco:

 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;

Tutaj zaktualizowaliśmy nasz komponent, aby otrzymać dodatkową opcjonalną właściwość, którą jest obiekt błędu z RHF, i wyświetlamy komunikat o błędzie w zwróconym polu wejściowym, gdy wystąpi błąd. Ostatnia rzecz, dodaj obiekt errors do zdestrukturyzowanego obiektu i zaktualizuj komponent w swojej pętli:

 const { control, handleSubmit, errors } = useForm({ validationSchema, });
 {formFields.map((field, index) => ( <Input {...field} control={control} key={index} errors={errors} /> ))}
Formularz rejestracyjny z komunikatami o błędach (iOS) (duży podgląd)

Twoje formularze dostarczają teraz wizualnych wskazówek, gdy użytkownik nie robi czegoś dobrze. Tak pozwala zmienić komunikat o błędzie. Możesz to zrobić, przekazując ciąg do używanej metody walidacji. Na przykład w przypadku poczty e-mail możesz wykonać następujące czynności:

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

Poprawa dostępności

Komponenty Ionica są zwykle nakładkami na odpowiedni element natywny, co oznacza, że ​​akceptują większość – jeśli nie wszystkie – istniejących atrybutów tego elementu. Możesz poprawić swoje pola wprowadzania i uczynić je bardziej dostępnymi dla użytkowników niedowidzących, ustawiając atrybuty ARIA z odpowiednim tekstem.

Aby kontynuować nasz przykładowy formularz rejestracyjny, otwórz plik Input.tsx i wprowadź następujące zmiany:

 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;

Domyślny komponent IonInput , który przekazujemy do Controller , zawiera teraz atrybut aria-invalid , który wskazuje, czy pole zawiera błąd, oraz atrybut aria-describedby , który wskazuje odpowiedni komunikat o błędzie. Komunikat o błędzie jest teraz opakowany w span z rolą ARIA ustawioną na „błąd”. Teraz, gdy Twoje pole zawiera błąd, czytnik ekranu podświetli to pole i odczyta komunikat o błędzie.

  • Tutaj znajdziesz repozytorium GitHub.

Wniosek

Gratulacje! Nauczyłeś się budować i sprawdzać formularze podczas tworzenia aplikacji wieloplatformowych przy użyciu Ionic. Widziałeś również, jak łatwo jest udostępnić pola wejściowe użytkownikom z wadami wzroku. Mamy nadzieję, że ten samouczek zapewnia solidną platformę, z której możesz korzystać podczas tworzenia formularzy w aplikacjach Ionic React. Istnieją inne komponenty do tworzenia formularzy (takie jak select i radia), których nie omówiliśmy w tym samouczku, ale możesz znaleźć i przeczytać o nich więcej w oficjalnych dokumentach.

Bibliografia

  • Dokumentacja Ionic Framework
  • Reaguj formularz haka
  • Tak, dokumenty
  • Phil Haack o weryfikacji adresów e-mail
  • Dostępność w MDN Web Docs