複雑なフォーム
複雑なフォームとは?
複雑なフォームは、通常、単純なキーと値のペア以上の構造のデータを含みます。たとえば、それぞれにメールアドレス、電話番号、住所を持つ人々のリストなどです。これは基本的にオブジェクトの配列になります。
入力の命名
オブジェクト
ネストされた項目は、名前の中で.
(ドット)で項目を区切ることで作成できます。たとえば、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)"
]
}