モジュラーフォーム
モジュラーフォームは、Qwik 上にネイティブに構築された型安全なフォームライブラリです。ヘッドレス設計により、フォームの視覚的な外観を完全に制御できます。このライブラリは、状態管理と入力検証を行います。
開始するには、npm パッケージをインストールします
npm install @modular-forms/qwik
フォームを定義する
フォームの作成を開始する前に、フィールドの構造とデータ型を定義します。モジュラーフォームは、文字列だけでなく、ブール値、数値、ファイル、日付、オブジェクト、配列も処理できます。
type LoginForm = {
email: string;
password: string;
};
モジュラーフォームは入力検証にValibotとZodをサポートしているため、オプションでスキーマから型の定義を派生させることができます。
import * as v from 'valibot';
const LoginSchema = v.object({
email: v.pipe(
v.string(),
v.nonEmpty('Please enter your email.'),
v.email('The email address is badly formatted.'),
),
password: v.pipe(
v.string(),
v.nonEmpty('Please enter your password.'),
v.minLength(8, 'Your password must have 8 characters or more.'),
),
});
type LoginForm = v.InferInput<typeof LoginSchema>;
このガイドが Zod よりも Valibot を優先している理由が気になる場合は、この発表記事を読むことをお勧めします。
初期値を設定する
型の定義を作成したら、フォームの初期値の設定に進みます。これを行うには、routeLoader$
を作成し、以前に作成した型をジェネリックとして使用します。
export const useFormLoader = routeLoader$<InitialValues<LoginForm>>(() => ({
email: '',
password: '',
}));
routeLoader$
では、空の文字列の代わりに、データベースから値をクエリして渡すこともできます。渡されたオブジェクトに基づいて、フォームのストアが初期化され、Qwik がサーバー上でウェブサイトを確実にプリレンダリングできるようになります。初期値は、ユーザー入力後にフィールドの値が変更されたかどうかを確認するためにも後で使用されます。
フォームを作成する
フォームを作成するには、useForm
フックを使用します。これは、フォームのストアと、Form
、Field
、FieldArray
コンポーネントを含むオブジェクトを返します。パラメーターとして、以前に作成したローダーを持つオブジェクトをuseForm
に渡します。
export default component$(() => {
const [loginForm, { Form, Field, FieldArray }] = useForm<LoginForm>({
loader: useFormLoader(),
});
});
loginForm
オブジェクトを使用して、フォームの現在の状態にアクセスできます。さらに、reset
やsetValue
など、ライブラリによって提供されるさまざまなメソッドに渡して、状態を手動で変更できます。
コンポーネントの JSX 部分では、Form
コンポーネントを使用します。これは、フォームのフィールドを囲み、そのプロパティを介して、フォームが送信されたときに何が起こるかを定義できます。
export default component$(() => {
const [loginForm, { Form, Field, FieldArray }] = useForm<LoginForm>({
loader: useFormLoader(),
});
return <Form>…</Form>;
});
フォームフィールドを追加する
これで、フォームのフィールドの処理に進むことができます。Field
コンポーネントとFieldArray
コンポーネントを使用して、フィールドまたはフィールド配列を登録します。どちらのコンポーネントもヘッドレスであり、現在の状態への直接アクセスを提供します。レンダリングプロップの 2 番目のパラメーターは、フォームに接続するために、<input />
、<select />
、または<textarea />
要素に渡す必要があります。
<Form>
<Field name="email">
{(field, props) => (
<input {...props} type="email" value={field.value} />
)}
</Field>
<Field name="password">
{(field, props) => (
<input {...props} type="password" value={field.value} />
)}
</Field>
<button type="submit">Login</button>
</Form>
この API 設計により、完全にタイプセーフなフォームが実現します。さらに、ユーザーインターフェイスを完全に制御できます。独自のTextInput
コンポーネントを開発したり、事前に構築されたコンポーネントライブラリを接続したりできます。
入力検証
モジュラーフォームのコア機能の 1 つは、入力検証です。これには、Valibot または Zod スキーマ、または内部の検証関数を使用できます。このガイドをシンプルに保つために、以前に作成した Valibot スキーマを使用し、useForm
フックに渡します。
valiForm$
は、Valibot のエラーメッセージをモジュラーフォームで期待される形式に変換するアダプターです。Zod の場合は代わりにzodForm$
を使用します。
const [loginForm, { Form, Field, FieldArray }] = useForm<LoginForm>({
loader: useFormLoader(),
validate: valiForm$(LoginSchema),
});
これで、エラーが発生した場合に、フィールドのエラーメッセージを表示するだけで済みます。
<Field name="email">
{(field, props) => (
<div>
<input {...props} type="email" value={field.value} />
{field.error && <div>{field.error}</div>}
</div>
)}
</Field>
送信を処理する
最後のステップでは、フォームを送信する際に、関数を介して値にアクセスして、さらに処理して使用するだけです。このためには、formAction$
またはForm
コンポーネントのonSubmit$
プロパティを使用できます。
export const useFormAction = formAction$<LoginForm>((values) => {
// Runs on server
}, valiForm$(LoginSchema));
export default component$(() => {
const [loginForm, { Form, Field }] = useForm<LoginForm>({
loader: useFormLoader(),
action: useFormAction(),
validate: valiForm$(LoginSchema),
});
const handleSubmit = $<SubmitHandler<LoginForm>>((values, event) => {
// Runs on client
});
return (
<Form onSubmit$={handleSubmit}>
…
</Form>
);
});
最終的なフォーム
ここで、すべての構成要素をまとめると、動作するログインフォームが得られます。以下に、組み立てられたコードを示し、添付のサンドボックスで試すことができます。
// @ts-nocheck
/* eslint-disable @typescript-eslint/no-unused-vars */
import { $, component$, type QRL } from '@builder.io/qwik';
import { routeLoader$ } from '@builder.io/qwik-city';
import type { InitialValues, SubmitHandler } from '@modular-forms/qwik';
import { formAction$, useForm, valiForm$ } from '@modular-forms/qwik';
import * as v from 'valibot';
const LoginSchema = v.object({
email: v.pipe(
v.string(),
v.nonEmpty('Please enter your email.'),
v.email('The email address is badly formatted.'),
),
password: v.pipe(
v.string(),
v.nonEmpty('Please enter your password.'),
v.minLength(8, 'Your password must have 8 characters or more.'),
),
});
type LoginForm = v.InferInput<typeof LoginSchema>;
export const useFormLoader = routeLoader$<InitialValues<LoginForm>>(() => ({
email: '',
password: '',
}));
export const useFormAction = formAction$<LoginForm>((values) => {
// Runs on server
}, valiForm$(LoginSchema));
export default component$(() => {
const [loginForm, { Form, Field }] = useForm<LoginForm>({
loader: useFormLoader(),
action: useFormAction(),
validate: valiForm$(LoginSchema),
});
const handleSubmit: QRL<SubmitHandler<LoginForm>> = $((values, event) => {
// Runs on client
console.log(values);
});
return (
<Form onSubmit$={handleSubmit}>
<Field name="email">
{(field, props) => (
<div>
<input {...props} type="email" value={field.value} />
{field.error && <div>{field.error}</div>}
</div>
)}
</Field>
<Field name="password">
{(field, props) => (
<div>
<input {...props} type="password" value={field.value} />
{field.error && <div>{field.error}</div>}
</div>
)}
</Field>
<button type="submit">Login</button>
</Form>
);
});
まとめ
モジュラーフォームの基本を学び、最初の単純なフォームを作成する準備ができました。詳細と詳細については、ウェブサイトでさらに多くのガイドと API リファレンスを見つけることができます: modularforms.dev
今のところモジュラーフォームは気に入っていますか? GitHubでスターをいただけると大変光栄です!