IonicReactのフォームと検証
公開: 2022-03-10Ionic Frameworkは、HTML、CSS、およびJavaScriptを使用してクロスプラットフォームのモバイルアプリケーションを構築するためのUIツールキットです。 2020年初頭のIonic5のリリースには、Reactの公式サポートが含まれており、React開発者はお気に入りのツールを使用してモバイルアプリケーションを簡単に構築できます。 ただし、フォームの操作はあまりサポートされておらず、Reactエコシステムでフォームを構築するために利用できる既存のライブラリの多くは、IonicFrameworkのコンポーネントとうまく連携していません。
このチュートリアルでは、IonicReactのUI入力コンポーネントを使用してフォームを作成する方法を学習します。 また、ライブラリを使用して、フォーム入力の変更を検出し、検証ルールに応答する方法についても学習します。 最後に、入力のARIA属性に役立つテキストを追加することにより、スクリーンリーダーがフォームにアクセスできるようにする方法を学習します。
Ionicのフォームコンポーネント
フォームは、今日のほとんどのWebおよびモバイルアプリケーションの重要な部分です。 ユーザー登録およびログインフォームを介してアプリケーションの制限された部分へのアクセスを有効にする場合でも、ユーザーからフィードバックを収集する場合でも、アプリケーションのライフサイクルのある時点でフォームを作成する必要があります。
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をリッスンしているためだとonChange
ます。
Reactフックフォーム:小さくて速いReactフォームライブラリ
ありがたいことに、それはすべての運命と暗闇ではありません。 私は最近、Reactプロジェクトでフォームを操作するためのライブラリであるReact Hook Form(RHF)に出くわしました。 制御されたコンポーネントまたは制御されていないコンポーネントと入力検証のサポートを提供し、APIはフックベースであるため、機能コンポーネントでのみ機能します。
Ionic React開発者にとって最も魅力的な機能は、私の意見では、制御されたコンポーネントを操作するために提供するラッパー<Controller />
コンポーネントです。 コンポーネントにはonChangeName
があり、これを使用して、渡すコンポーネントインスタンスの変更イベント名を指定できます。 次のセクションでは、これによりIonicでのフォームの操作が非常に簡単になる方法を説明します。
サインアップフォームの作成
Ionicで登録フォームを作成するときに、RHFがフォーム機能にどのように役立つかを見てみましょう。 最新バージョンのIonicCLIを実行している場合( npm i -g @ionic/cli
を実行して確認)、次のコマンドを実行してReactで新しいIonicアプリを起動します。
ionic start myApp blank --type=react
ここでは空白のテンプレートを使用しました。 特にコンポーネントが機能コンポーネントとして記述されている場合は、Reactフックフォームライブラリを簡単に使用できるように既存のフォームを書き直すことができるはずです。
注:このチュートリアルを続行する前に、 ExploreContainer
コンポーネントとHome.tsxでのそのインポートを削除する必要があります。
フォームの使用を開始するには、プロジェクトのルートディレクトリで次のコマンドを実行してReact HookFormパッケージをインストールします。
yarn add react-hook-form
これにより、React HookFormライブラリがプロジェクトで利用できるようになります。 ライブラリを使用してフォーム入力フィールドを作成しましょう。 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;
これにより、電子メールアドレスを収集するための単一のフィールドを持つフォームが提供されます。 重要な部分を分解してみましょう(コードブロックで強調表示されています)。
まず、RHFからのuseForm()
フックの戻り値を分解します。 handleSubmit
は、フォームが検証に合格したときに、指定したハンドラー関数に入力の値を渡します。 control
は、制御されたコンポーネントをRHFに登録するために使用されるメソッドを含むオブジェクトです。
次に、標準のフォームアイテムブロックがありますが、ログインフォームの例とは異なり、 IonInput
コンポーネントをRHFの<Controller />
コンポーネントに渡し、 <Controller />
のonChangeName
をIonicの変更イベントに設定して変更イベントを登録します。名前を付け、 useForm()
の呼び出しからコントロールオブジェクトにcontrol
プロップを設定します。
これは今のところ良いことですが、ほぼ同じコードを何度も繰り返すことに気付くかもしれません。 与えられたプロパティで入力フィールドを構築する再利用可能なInput
コンポーネントを作成することを試みることができます。
Input.tsxという名前のsrc / componentsディレクトリにファイルを作成し、次のコードをファイルに追加します。
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} /> ))}
ユーザーが正しいことをしていないときに、フォームが視覚的な手がかりを提供するようになりました。 うん、エラーメッセージを変更することができます。 これを行うには、使用している検証メソッドに文字列を渡します。 電子メールの場合、例として、次のことができます。
{ 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;
Controller
に渡すデフォルトのIonInput
コンポーネントには、フィールドにエラーがあるかどうかを示すaria-invalid
属性と、対応するエラーメッセージを指すaria-describedby
属性が含まれています。 エラーメッセージは、ARIAロールが「エラー」に設定されたspan
でラップされるようになりました。 これで、フィールドにエラーが発生すると、スクリーンリーダーがそのフィールドを強調表示し、エラーメッセージを読み上げます。
- ここにGitHubリポジトリがあります。
結論
おめでとう! Ionicを使用してクロスプラットフォームアプリを構築するときにフォームを構築および検証する方法を学習しました。 また、視覚障害のあるユーザーが入力フィールドにアクセスできるようにするのがいかに簡単かを見てきました。 うまくいけば、このチュートリアルは、IonicReactアプリでフォームを作成するときに使用できる堅固なプラットフォームを提供します。 このチュートリアルでは検討しなかったフォームを作成するための他のコンポーネント(選択やラジオなど)がありますが、公式ドキュメントでそれらを見つけて読むことができます。
参考文献
- Ionicフレームワークドキュメント
- Reactフックフォーム
- うんドキュメント
- 電子メールアドレスの検証に関するPhilHaack
- MDN WebDocsのアクセシビリティ