This page dives into nested form arrays, a powerful approach for handling collections of data within forms. Nested form arrays allow you to dynamically add, remove, and manage items within a list, ideal for scenarios like product selections or order details.
Imagine an e-commerce checkout form where users can add multiple items to their cart. Each item might have properties like name, quantity, and price. A nested form array would be perfect for capturing this dynamic data.
Here's a breakdown of creating a nested form array with validation using @nattyjs/tidy
Start by defining a model that reflects the data structure. We'll have a user object with a cart property as an array of objects representing each cart item.
const user = {
name: "",
email: "",
cart: [],
};
Use the useForm function with nested form array syntax to define validation rules for both the top-level user object and the items within the cart array.
import { useForm, Validators, formArray } from "@nattyjs/tidy";
const userForm = useForm(user, {
validators: {
name: Validators.required(),
email: [Validators.required(), Validators.email()],
cart: formArray((item) => ({
name: Validators.required(),
quantity: [Validators.required(), Validators.min(1)], // Minimum quantity of 1
price: [Validators.required(), Validators.numeric()],
})),
},
});
Integrate the form object into your template:
<div>
<input type="text" v-model="userForm.name" />
<input type="text" v-model="userForm.email" />
<h3>Cart Items</h3>
<ul>
<li v-for="(cartItem, index) in userForm.cart" :key="index">
<input type="text" v-model="cartItem.name" /> (Name)
<input type="number" v-model="cartItem.quantity" /> (Quantity)
<input type="text" v-model="cartItem.price" /> (Price)
<button @click="removeItem(index)">Remove Item</button>
</li>
</ul>
<button @click="addItem">Add Item</button>
</div>
Implement methods in your component to handle adding and removing items from the cart array using your framework's specific syntax. Here's a general idea:
methods: {
addItem() {
userForm.cart.push(userForm.controlFactory('cart')); // Create a new empty cart item
},
removeItem(index) {
userForm.cart.splice(index, 1); // Remove the item at the specified index
},
},
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 { useForm, Validators } from '@nattyjs/tidy'
const user = {
name: '',
email: '',
age: 0,
}
const userFrom = useForm(user, {
validators: {
name: Validators.required(),
email: [Validators.required(), Validators.email()],
age: [Validators.required(), Validators.numeric()],
},
})
</script>
<template>
<div>
<input v-model="userFrom.name" type="text">
<span>{{ userFrom.name.errorMessage }}</span>
<input v-model="userFrom.email" type="text">
<span>{{ userFrom.email.errorMessage }}</span>
<input v-model="userFrom.age" type="text">
<span>{{ userFrom.age.errorMessage }}</span>
<button :disabled="!userFrom.valid">
Submit
</button>
</div>
</template>