This page explores creating nested forms, a technique for handling complex data structures within forms. Nested forms allow you to group related fields and manage their validation independently.
Imagine a user registration form where you want to collect not only basic information (name, email) but also a billing address with separate fields for street, city, state, and zip code. A nested form structure would be ideal for this scenario.
Here's a breakdown of creating a nested form.
Start by defining a model structure that reflects the nested data you want to collect. In this case, we'll have a user object with a nested billingAddress object.
const user = {
name: "",
email: "",
billingAddress: {
street: "",
city: "",
state: "",
zip: "",
},
};
Use the useForm function and define validation rules for both the top-level user object and the nested billingAddress object.
import { useForm, Validators } from "@nattyjs/tidy";
const userForm = useForm(user, {
validators: {
name: Validators.required(),
email: [Validators.required(), Validators.email()],
billingAddress: {
street: Validators.required(),
city: Validators.required(),
state: Validators.required(),
zip: [Validators.required(), Validators.pattern({expression:{digit:/^\d{5}$/}})], // US zip code pattern
},
},
});
Integrate the form object into your HTML template, using dedicated sections for the top-level fields and nested address fields (adapt the syntax to your framework):
<template>
<div>
<input type="text" v-model="userForm.name" />
<input type="text" v-model="userForm.email" />
<h3>Billing Address</h3>
<input type="text" v-model="userForm.billingAddress.street" />
<input type="text" v-model="userForm.billingAddress.city" />
<input type="text" v-model="userForm.billingAddress.state" />
<input type="text" v-model="userForm.billingAddress.zip" />
<button :disabled="userForm.valid">Submit</button>
</div>
<template>
Use the errorMessage property of the corresponding FormControl within the userForm object to display error messages for both top-level and nested fields.
The submit button should remain disabled until both the top-level and nested forms are valid. Adapt the code from the Simple Form page to your specific framework.
<button :disabled="!userForm.valid">Submit</button>
<script setup lang="ts">
import { reactive } from 'vue';
import { useForm, Validators } from '@nattyjs/tidy';
const user = {
name: "",
email: "",
billingAddress: {
street: "",
city: "",
state: "",
zip: "",
},
};
const userForm = reactive(
useForm(user, {
validators: {
name: Validators.required(),
email: [Validators.required(), Validators.email()],
billingAddress: {
street: Validators.required(),
city: Validators.required(),
state: Validators.required(),
zip: [Validators.required(), Validators.pattern({expression:{digit:/^\d{5}$/}})], // US zip code pattern
},
},
})
);
</script>
<template>
<div>
<input type="text" v-model="userForm.name" />
<input type="text" v-model="userForm.email" />
<h3>Billing Address</h3>
<input type="text" v-model="userForm.billingAddress.street" />
<input type="text" v-model="userForm.billingAddress.city" />
<input type="text" v-model="userForm.billingAddress.state" />
<input type="text" v-model="userForm.billingAddress.zip" />
<button :disabled="userForm.valid">Submit</button>
</div>
</template>