複雑なフォーム

複雑なフォームとは?

複雑なフォームは、通常、単純なキーと値のペア以上の構造のデータを含みます。たとえば、それぞれにメールアドレス、電話番号、住所を持つ人々のリストなどです。これは基本的にオブジェクトの配列になります。

入力の命名

オブジェクト

ネストされた項目は、名前の中で.(ドット)で項目を区切ることで作成できます。たとえば、person.name{ person: { name: 'sam' } }に変換されます。

配列

最上位レベルまたはネストされた配列は、名前の中で0から始まるインデックスを指定することで作成されます。たとえば、person.pets.0{ person: { pets: [ 'cat' ] } }に変換されます。

複雑なフォームを作成する鍵は、入力の命名にあります。以下は、上記で説明したリストの例です。

<form>
  <input name="person.0.name" value="Sam">
  <input name="person.0.email" value="[email protected]">
  <input name="person.0.pets.0" value="cat">
  <input name="person.0.pets.1" value="dog">
  <input name="person.0.address.street" value="1234 Example Ave.">
  <input name="person.0.address.city" value="Qwik">
  <input name="person.0.address.state" value="IA">
  <input name="person.0.address.zip" value="00000">
  <input name="person.0.pets.0" value="beaver">
 
  <input name="person.1.name" value="Bonnie">
  <input name="person.1.email" value="[email protected]">
  <input name="person.1.address.street" value="768 Resolution Way">
  <input name="person.1.address.city" value="Jaffa">
  <input name="person.1.address.state" value="IL">
  <input name="person.1.address.zip" value="01948">
</form>

出力オブジェクト

フォームを送信した後、データは次のようなオブジェクトに解析されます。

{
  "person": [
    {
      "name": "Sam",
      "email": "[email protected]",
      "address": {
        "street": "1234 Example Ave.",
        "city": "Qwik",
        "state": "IA",
        "zip": "00000"
      },
      "pets": ["beaver"]
    },
    {
      "name": "Bonnie",
      "email": "[email protected]",
      "address": {
        "street": "768 Resolution Way",
        "city": "Jaffa",
        "state": "IL",
        "zip": "01948"
      }
    }
  ]
}

アクションとの連携

複雑なフォームは、routeAction$とglobalAction$でzod$を使用して検証できます。前の例を続けると、次のようになります。

export const action = routeAction$(
  async (person) => {
    return { success: true, person, };
  },
  // Zod schema is used to validate the FormData
  zod$({
    person: z.array(
      z.object({
        name: z.string(),
        email: z.string().email(),
        address: z.object({
          street: z.string(),
          city: z.string(),
          state: z.string(),
          zip: z.coerce.number()
        }),
        pets: z.array(z.string())
      })
    ),
  })
);

フィールドエラーもドット表記で(ほぼ)

ドット表記を使用する場合、エラーメッセージもfieldErrorsプロパティでドット表記で返されます。これにより、入力名とfieldErrorキーが一致するという利点があります。

このアクションの場合

export const addPersonAction = routeAction$(
  async person => {
    return { success: true, person };
  },
  // Zod schema is used to validate the FormData
  zod$({
    person: z.object({
      name: z.string(),
      email: z.string().email(),
      address: z.object({
        street: z.string(),
        city: z.string(),
        state: z.string(),
        zip: z.coerce.number(),
      }),
      pets: z.array(z.string()),
    }),
  })
);

すべてが間違っている場合、この種のfieldErrorsが得られます。

{
  "person.name": "Invalid string",
  "person.email": "Invalid email",
  "person.address.street": "Invalid string",
  "person.address.city": "Invalid string",
  "person.address.state": "Invalid string",
  "person.address.zip": "Invalid number",
  "person.pets[]": ["Required"]
}

personを配列にすると、エラーメッセージは少し異なる表記に切り替わります。

{
  "person[].name": ["Invalid string"],
  "person[].email": ["Invalid email"],
  "person[].address.street": ["Invalid string"],
  "person[].address.city": ["Invalid string"],
  "person[].address.state": ["Invalid string"],
  "person[].address.zip": ["Invalid number"],
  "person[].pets[]": ["Required"]
}

配列型に値を全く割り当てないとしましょう、フォームでpersonを完全に忘れたとします。その場合、エラーはfieldErrors["person[]"]にあります。

このようにして、エラーメッセージを入力名と簡単に照合できます。

export const useAddPersonAction = routeAction$(
  async person => {
    console.log(person);
    return { success: true, person };
  },
  zod$({
    person: z.object({
      name: z.string().min(2),
      email: z.string().email(),
      address: z.object({
        street: z.string().min(2),
        city: z.string().min(2),
        state: z.string().min(2),
        zip: z.coerce.number(),
      }),
      pets: z.array(z.string().min(2)),
    }),
  })
);
 
export default component$(() => {
  const testAction = useAddPersonAction();
 
  const renderError = (errorMessage: string | undefined) => {
    if (!errorMessage) return null;
    return <p class="error">{errorMessage}</p>;
  };
 
  return (
    <Form action={testAction}>
      <input type="email" name="person.email" placeholder="Email" />
      {renderError(testAction.value?.fieldErrors?.["person.email"])}
      <input type="text" name="person.name" placeholder="Name" />
      {renderError(testAction.value?.fieldErrors?.["person.name"])}
      <input type="text" name="person.address.street" placeholder="Street" />
      {renderError(testAction.value?.fieldErrors?.["person.address.street"])}
      <input type="text" name="person.address.city" placeholder="City" />
      {renderError(testAction.value?.fieldErrors?.["person.address.city"])}
      <input type="text" name="person.address.state" placeholder="State" />
      {renderError(testAction.value?.fieldErrors?.["person.address.state"])}
      <input type="text" name="person.address.zip" placeholder="Zip" />
      {renderError(testAction.value?.fieldErrors?.["person.address.zip"])}
      <input type="text" name="person.pets.0" placeholder="Pet 1" />
      {renderError(testAction.value?.fieldErrors?.["person.pets[]"]?.[0])}
      <button>Send</button>
    </Form>
  );
});

この例では、fieldErrorsは次のようになります。

{
  "person.name": "String must contain at least 2 character(s)",
  "person.email": "Invalid email",
  "person.address.street": "String must contain at least 2 character(s)",
  "person.address.city": "String must contain at least 2 character(s)",
  "person.address.state": "String must contain at least 2 character(s)",
  "person.pets[]": [
    "String must contain at least 2 character(s)"
  ]
}

貢献者

このドキュメントの改善に貢献してくれたすべての貢献者に感謝します!

  • ulic75
  • hamatoyogi
  • aendel
  • tzdesign