Формы и проверка в Ionic React
Опубликовано: 2022-03-10Ionic Framework — это набор инструментов пользовательского интерфейса для создания кроссплатформенных мобильных приложений с использованием HTML, CSS и JavaScript. Выпуск Ionic 5 в начале 2020 года сопровождался официальной поддержкой React, что позволило разработчикам React легко создавать мобильные приложения, используя свои любимые инструменты. Однако поддержки работы с формами не так много, и многие из существующих библиотек, доступных для создания форм в экосистеме React, не очень хорошо работают с компонентами Ionic Framework.
В этом руководстве вы узнаете, как создавать формы с использованием компонентов ввода пользовательского интерфейса Ionic React. Вы также узнаете, как использовать библиотеку для обнаружения изменений ввода формы и реагирования на правила проверки. Наконец, вы научитесь делать свои формы доступными для программ чтения с экрана, добавляя полезный текст к атрибутам ARIA ваших входных данных.
Компоненты формы Ionic
Сегодня формы являются важной частью большинства веб-приложений и мобильных приложений. Независимо от того, разрешаете ли вы доступ к ограниченным частям вашего приложения через формы регистрации пользователей и входа в систему или собираете отзывы от ваших пользователей, вы должны — в какой-то момент жизненного цикла вашего приложения — создать форму.
Ionic предоставляет готовые компоненты для работы с формами, некоторые из которых включают IonItem
, IonLabel
, IonInput
, IonCheckbox
и IonRadio
. Мы можем комбинировать эти компоненты для создания стандартных форм, не добавляя никаких стилей самостоятельно.
Например, следующий код:
<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>
Даст нам форму входа, которая выглядит так:

По умолчанию компоненты формы Ionic отлично смотрятся на iOS или Android, но они могут быть немного громоздкими, если вы работаете с React. Как и в случае с большинством инструментов в экосистеме React, вы должны решить, как вы хотите создавать свои формы, когда речь идет о функциональности и доступности — и то, и другое так же важно, как и дизайн.
Хотя на выбор уже доступно так много помощников форм React, большинство из них не работают с компонентами формы Ionic. Я подозреваю, что основная причина этого заключается в том, что событие, срабатывающее при изменении значения поля в Ionic, — это onIonChange
, тогда как большинство существующих библиотек форм прослушивают onChange
.

React Hook Form: библиотека небольших и быстрых форм React
К счастью, это не все гибель и мрак. Недавно я наткнулся на React Hook Form (RHF) — библиотеку для работы с формами в проектах React. Он обеспечивает поддержку контролируемых и неконтролируемых компонентов и проверку ввода, а API основан на ловушках, поэтому работает только с функциональными компонентами.
На мой взгляд, самая привлекательная функция для разработчиков Ionic React — это компонент-оболочка <Controller />
, который он предоставляет для работы с контролируемыми компонентами. Компонент имеет onChangeName
, которое можно использовать для указания имени события изменения для любого экземпляра компонента, который вы ему передаете. В следующих разделах я покажу вам, как это упрощает работу с формами в Ionic.
Создание формы регистрации
Давайте посмотрим, как RHF помогает нам с функциональностью формы, когда мы создаем регистрационную форму в Ionic. Если вы используете последнюю версию Ionic CLI (запустите npm i -g @ionic/cli
для подтверждения), запустите новое приложение Ionic с React, выполнив следующую команду:
ionic start myApp blank --type=react
Здесь я использовал пустой шаблон. Вы должны быть в состоянии переписать существующие формы, чтобы с легкостью использовать библиотеку форм React Hook, особенно если ваши компоненты написаны как функциональные компоненты.
Примечание. Прежде чем приступить к выполнению этого руководства, вам следует удалить компонент ExploreContainer
и его импорт в Home.tsx.
Чтобы начать работу с формой, установите пакет React Hook Form, выполнив следующую команду в корневом каталоге вашего проекта:
yarn add react-hook-form
Это сделает библиотеку React Hook Form доступной в вашем проекте. Создадим поле ввода формы с помощью библиотеки. Откройте файл Home.tsx и замените его содержимое следующим:
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;
Это дает вам форму с одним полем для сбора адреса электронной почты. Давайте разберем важные части (выделены в блоке кода).
Во-первых, мы деструктурируем возвращаемое значение useForm()
из RHF. handleSubmit
передает введенные вами значения функции обработчика, которую вы указываете, когда форма проходит проверку. control
— объект, содержащий методы, используемые для регистрации управляемых компонентов в RHF.
Затем у нас есть стандартный блок элемента формы, но в отличие от примера для формы входа мы передаем компонент IonInput
компоненту <Controller />
RHF, регистрируем событие изменения, устанавливая свойство <Controller />
onChangeName
для события изменения Ionic. name и установите свойство control
для объекта управления из вызова useForm()
.
Пока это хорошо, но вы можете повторять почти один и тот же код снова и снова. Вы можете попытаться создать многоразовый компонент Input
, который создает поле ввода с заданными свойствами.
Создайте файл в каталоге src/components с именем Input.tsx и добавьте в него следующий код:
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;
Этот компонент получает свойство name
и необязательный control
, component
и label
, а также отображает поле ввода с использованием компонентов формы Ionic, представленных ранее. Это уменьшает объем кода, который необходимо написать при создании полей ввода формы. Вы можете закончить остальную часть формы, используя этот компонент. Отредактируйте файл Home.tsx, внеся следующие изменения:

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;
С вашей настройкой до сих пор у вас есть массив полей ввода вашей формы ( name
является единственным обязательным свойством), причем каждое поле отображается с использованием компонента Input
ранее. Вы можете пойти еще дальше и сохранить данные поля в файле JSON, сохранив код внутри ваших компонентов с формами в чистоте. На данный момент ваше приложение (работающее по адресу https://localhost:8100 с командой ionic serve
) должно выглядеть так:

Как насчет проверки поля?
Вы могли заметить, что поля ввода нашей формы еще не имеют никакой логики проверки. Если бы это было приложение, предназначенное для реального использования, это могло бы привести ко многим нежелательным эффектам, если только ваш API не настроен для проверки входящих данных. Кстати, ваш API всегда должен проверять входящие данные.
RHF поставляется с проверкой, которая соответствует стандарту HTML для встроенной проверки формы. Это отлично работает для простой проверки, например, для обязательного заполнения поля или установки минимальной и максимальной длины поля. Если вы хотите использовать сложную логику проверки, я бы рекомендовал использовать Yup. Хотя вы можете использовать любую библиотеку проверки схемы объекта, RHF поддерживает Yup из коробки.
Выполните следующую команду, чтобы установить библиотеку (и типизацию):
yarn add yup @types/yup
Затем добавьте это в импорт вашего компонента:
import { object, string } from 'yup'; const Home: React.FC = () => { ... }
Затем добавьте следующий код вверху вашего компонента:
const Home: React.FC = () => { const validationSchema = object().shape({ email: string().required().email(), fullName: string().required().min(5).max(32), password: string().required().min(8), }); // ... }
Здесь мы создали схему объекта и добавили правила проверки для каждого свойства с помощью yup
. Имена в объекте должны совпадать с именами в тегах ввода вашей формы, иначе ваши правила не будут активированы.
Наконец, обновите useForm()
, чтобы использовать схему, которую мы определили, установив свойство validationSchema
следующим образом:
const { control, handleSubmit } = useForm({ validationSchema, });
Теперь, когда вы нажимаете кнопку отправки, обработчик handleSubmit
не вызывается и данные формы не отправляются. Хотя это именно то, что мы хотели, похоже, что пользователь не может знать, что происходит. Давайте исправим это, показав текстовые подсказки, когда поле заполнено неправильно.
Сначала обновите компонент Input
, чтобы он выглядел следующим образом:
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;
Здесь мы обновили наш компонент, чтобы получить дополнительное необязательное свойство, которое является объектом ошибки от RHF, и мы отображаем сообщение об ошибке в возвращаемом поле ввода всякий раз, когда возникает ошибка. И последнее, добавьте объект ошибок в свой деструктурированный объект и обновите компонент в своем цикле:
const { control, handleSubmit, errors } = useForm({ validationSchema, });
{formFields.map((field, index) => ( <Input {...field} control={control} key={index} errors={errors} /> ))}

Ваши формы теперь предоставляют визуальные подсказки, когда пользователь делает что-то неправильно. Yup позволяет изменить сообщение об ошибке. Вы можете сделать это, передав строку используемому методу проверки. Например, для электронной почты вы можете сделать следующее:
{ email: string() .email('Please provide a valid email address') .required('This is a required field'), }
Улучшение доступности
Компоненты Ionic обычно представляют собой оболочки над соответствующим нативным элементом, что означает, что они принимают большинство, если не все, существующих атрибутов этого элемента. Вы можете улучшить свои поля ввода и сделать их более доступными для слабовидящих пользователей, установив атрибуты ARIA с соответствующим текстом.
Чтобы продолжить наш пример регистрационной формы, откройте файл Input.tsx и внесите следующие изменения:
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;
Компонент IonInput
по умолчанию, который мы передаем в Controller
, теперь включает атрибут aria-invalid
, указывающий, есть ли в поле ошибка, и атрибут aria-describedby
, указывающий на соответствующее сообщение об ошибке. Сообщение об ошибке теперь заключено в span
, в котором для роли ARIA задано значение «ошибка». Теперь, когда в вашем поле есть ошибка, программа чтения с экрана выделит это поле и прочитает сообщение об ошибке.
- Вы найдете репозиторий GitHub здесь.
Заключение
Поздравляем! Вы узнали, как создавать и проверять формы при создании кроссплатформенных приложений с использованием Ionic. Вы также видели, как легко сделать ваши поля ввода доступными для пользователей с нарушением зрения. Надеемся, что это руководство предоставляет надежную платформу, которую вы можете использовать при создании форм в своих приложениях Ionic React. Существуют и другие компоненты для создания форм (такие как select и radios), которые мы не рассматривали в этом руководстве, но вы можете найти и прочитать о них больше в официальной документации.
использованная литература
- Документы Ionic Framework
- Реагировать на хук
- Документы
- Фил Хаак о проверке адресов электронной почты
- Доступность веб-документов MDN