Ionic React 中的表單和驗證

已發表: 2022-03-10
快速總結↬ Ionic Framework 為使用 React 的任何平台構建快速和移動優化的應用程序提供一流的支持。 在本教程中,您將學習如何在使用 Ionic React 時構建表單,以及如何通過添加帶有有用文本提示的驗證規則來使這些表單具有交互性。

Ionic Framework 是一個 UI 工具包,用於使用 HTML、CSS 和 JavaScript 構建跨平台移動應用程序。 2020 年初發布的 Ionic 5 附帶了對 React 的官方支持,使 React 開發人員能夠使用他們喜歡的工具輕鬆構建移動應用程序。 然而,對使用表單的支持並不多,而且在 React 生態系統中可用於構建表單的許多現有庫不能很好地與 Ionic Framework 的組件配合使用。

您將在本教程中學習如何使用 Ionic React 的 UI 輸入組件構建表單。 您還將學習如何使用庫來幫助檢測表單輸入更改和響應驗證規則。 最後,您將學習通過向輸入的 ARIA 屬性添加有用的文本,使屏幕閱讀器可以訪問您的表單。

Ionic 的表單組件

表單是當今大多數 Web 和移動應用程序的重要組成部分。 無論您是通過用戶註冊和登錄表單啟用對應用程序受限部分的訪問,還是從用戶那裡收集反饋,您都必須在應用程序生命週期的某個時刻構建一個表單。

Ionic 提供了用於處理表單的預構建組件——其中一些包括IonItemIonLabelIonInputIonCheckboxIonRadio 。 我們可以組合這些組件來構建標準外觀的表單,而無需自己添加任何樣式。

例如,下面的代碼:

 <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

字段更改時觸發的更改事件(大預覽)

React Hook Form:小型快速的 React 表單庫

值得慶幸的是,這並不全是厄運和悲觀。 我最近遇到了 React Hook Form (RHF),這是一個用於在 React 項目中處理表單的庫。 它提供對受控或不受控組件和輸入驗證的支持,並且 API 是基於鉤子的,因此它僅適用於功能組件。

在我看來,對 Ionic React 開發人員最有吸引力的特性是它提供的用於處理受控組件的包裝器<Controller />組件。 該組件有一個onChangeName屬性,可用於為您傳遞給它的任何組件實例指定更改事件名稱。 在接下來的部分中,我將向您展示這如何使在 Ionic 中使用表單變得非常容易。

跳躍後更多! 繼續往下看↓

建立一個註冊表單

讓我們看看當我們在 Ionic 中構建註冊表單時,RHF 如何幫助我們實現表單功能。 如果您正在運行最新版本的 Ionic CLI(運行npm i -g @ionic/cli進行確認),請通過運行以下命令使用 React 啟動新的 Ionic 應用程序:

 ionic start myApp blank --type=react

我在這裡使用了一個空白模板。 您應該能夠輕鬆地重寫現有表單以使用 React Hook Form 庫,尤其是當您的組件被編寫為功能組件時。

注意:在繼續本教程之前,您應該刪除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;

這為您提供了一個包含單個字段的表單來收集電子郵件地址。 讓我們分解重要的部分(在代碼塊中突出顯示)。

首先,我們從 RHF 中useForm()鉤子的返回值。 當表單通過驗證時, handleSubmit將您的輸入值傳遞給您指定的處理函數。 control是一個對象,其中包含用於將受控組件註冊到 RHF 中的方法。

接下來,我們有一個標準的表單項塊,但與登錄表單的示例不同,我們將IonInput組件傳遞給 RHF 的<Controller />組件,通過將<Controller />onChangeName屬性設置為 Ionic 的更改事件來註冊更改事件名稱,並通過調用useForm()將控件屬性設置為control對象。

到目前為止這很好,但是您可能會發現自己一遍又一遍地重複幾乎相同的代碼。 您可以嘗試製作一個可重用的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 prop 和可選的controlcomponentlabel props,並使用前面介紹的 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 文件中,從而使組件中的代碼保持整潔。 此時,您的應用程序(使用ionic serve命令在 https://localhost:8100 運行)應如下所示:

註冊表單頁面(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 角色設置為“error”的span包裝起來。 現在,當您的字段出現錯誤時,屏幕閱讀器將突出顯示該字段並讀出錯誤消息。

  • 你會在這裡找到 GitHub 存儲庫。

結論

恭喜! 您已經學習瞭如何在使用 Ionic 構建跨平台應用程序時構建和驗證表單。 您還看到讓有視覺障礙的用戶可以訪問您的輸入字段是多麼容易。 希望本教程提供了一個可靠的平台,供您在 Ionic React 應用程序中構建表單時使用。 還有其他用於構建表單的組件(例如 select 和 radios),我們沒有在本教程中探討,但您可以在官方文檔中找到並閱讀有關它們的更多信息。

參考

  • 離子框架文檔
  • 反應鉤子形式
  • 是的文檔
  • Phil Haack 談驗證電子郵件地址
  • MDN Web 文檔的可訪問性