Create Nested Form Arrays

This page introduces form validation concepts through creating a simple form with basic validation rules for user input.

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.

Use Case

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.

Creating a Nested Form Array with Validation

Here's a breakdown of creating a nested form array with validation using @nattyjs/tidy

1. Define the Form Model:

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: [],
};

2. Create the Nested Form with Validation

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()],
    })),
  },
});

3. Write HTML for Nested Array Input Controls

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>

4. Adding and Removing Items (Framework Specific):

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
  },
},

4. Show Validation Errors (similar to Simple Form)

Use the errorMessage property of the corresponding FormControl within the userForm object to display error messages for both top-level and nested fields.

5. Validate Form on Submit (similar to Simple Form):

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>

Complete Code Walkthrough

<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>