IonicReactのフォームと検証

公開: 2022-03-10
クイックサマリー↬IonicFrameworkは、Reactを使用して、あらゆるプラットフォーム向けに高速でモバイル向けに最適化されたアプリケーションを構築するためのファーストクラスのサポートを提供します。 このチュートリアルでは、Ionic Reactを使用してフォームを作成する方法と、役立つテキストヒントを含む検証ルールを追加してこれらのフォームをインタラクティブにする方法を学習します。

Ionic Frameworkは、HTML、CSS、およびJavaScriptを使用してクロスプラットフォームのモバイルアプリケーションを構築するためのUIツールキットです。 2020年初頭のIonic5のリリースには、Reactの公式サポートが含まれており、React開発者はお気に入りのツールを使用してモバイルアプリケーションを簡単に構築できます。 ただし、フォームの操作はあまりサポートされておらず、Reactエコシステムでフォームを構築するために利用できる既存のライブラリの多くは、IonicFrameworkのコンポーネントとうまく連携していません。

このチュートリアルでは、IonicReactのUI入力コンポーネントを使用してフォームを作成する方法を学習します。 また、ライブラリを使用して、フォーム入力の変更を検出し、検証ルールに応答する方法についても学習します。 最後に、入力のARIA属性に役立つテキストを追加することにより、スクリーンリーダーがフォームにアクセスできるようにする方法を学習します。

Ionicのフォームコンポーネント

フォームは、今日のほとんどのWebおよびモバイルアプリケーションの重要な部分です。 ユーザー登録およびログインフォームを介してアプリケーションの制限された部分へのアクセスを有効にする場合でも、ユーザーからフィードバックを収集する場合でも、アプリケーションのライフサイクルのある時点でフォームを作成する必要があります。

Ionicは、フォームを操作するためのビルド済みコンポーネントを提供します。その一部には、 IonItemIonLabelIonInputIonCheckbox 、および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>

次のようなログインフォームが表示されます。

iOSの標準ログインフォーム(大プレビュー)

箱から出してすぐに、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の小道具とオプションのcontrolcomponentlabelの小道具を受け取り、前に紹介した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コマンドを使用して実行)は次のようになります。

登録フォームページ(iOS)(大プレビュー)

フィールド検証はどうですか?

フォームの入力フィールドにはまだ検証ロジックがないことに気付いたかもしれません。 これが実際の使用を目的としたアプリである場合、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} /> ))}
エラーメッセージ付き登録フォーム(iOS)(大プレビュー)

ユーザーが正しいことをしていないときに、フォームが視覚的な手がかりを提供するようになりました。 うん、エラーメッセージを変更することができます。 これを行うには、使用している検証メソッドに文字列を渡します。 電子メールの場合、例として、次のことができます。

 { 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のアクセシビリティ