Demonstrates model lists and nested forms Async
<form id=""
class="pydantic-form needs-validation"
style=""
method="POST"
action="/pets"
novalidate>
<div class="mb-3"><label for="owner_name"><i class="bi bi-person"></i> Owner Name *</label>
<input name="owner_name" id="owner_name" class="form-control" value="Sarah Mitchell" required="required" minlength="2" maxlength="11" placeholder="Enter your full name" pattern="\d{3}-\d{2}-\d{4}" inputmode="numeric" autocomplete="off" type="text" />
<div class="form-text">Your full name as the pet owner</div></div>
<div class="mb-3"><label for="email"><i class="bi bi-envelope"></i> Email Address *</label>
<input name="email" id="email" class="form-control" value="sarah.mitchell@example.com" required="required" placeholder="your.email@example.com" inputmode="email" type="email" />
<div class="form-text">Your contact email address</div></div>
<div class="mb-3"><label for="address"><i class="bi bi-house"></i> Address</label>
<textarea name="address" id="address" class="form-control" placeholder="Enter your full address...">123 Main Street
Apt 4B
Springfield, IL 62701</textarea>
<div class="form-text">Your home address (optional)</div></div>
<div class="mb-3"><label for="emergency_contact"><i class="bi bi-person-exclamation"></i> Emergency Contact</label>
<input name="emergency_contact" id="emergency_contact" class="form-control" value="John Mitchell - (555) 123-4567" placeholder="Emergency contact name and phone" pattern="\d{3}-\d{2}-\d{4}" maxlength="11" inputmode="numeric" autocomplete="off" type="text" />
<div class="form-text">Someone to contact in case of emergency</div></div>
<div class="mb-3 model-list-block" data-field-name="pets" data-min-items="1" data-max-items="10">
<label class="form-label fw-bold">Your Pets <span class="text-danger">*</span></label>
<div class="model-list-container" data-field-name="pets" data-min-items="1" data-max-items="10">
<div class="model-list-items" id="pets-items">
<div class="model-list-item card border mb-3"
data-index="0"
data-title-template="Pet #{index}: {name}"
data-field-name="pets">
<div class="card-header d-flex justify-content-between align-items-center">
<h6 class="mb-0">
<button class="btn btn-link text-decoration-none p-0 text-start"
type="button"
data-bs-toggle="collapse"
data-bs-target="#pets_item_0_content"
aria-expanded="false"
aria-controls="pets_item_0_content">
<i class="bi bi-chevron-right me-2"></i>
<i class="bi bi-card-list me-2"></i>
Pet #1: Max
</button>
</h6>
<button type="button"
class="btn btn-outline-danger btn-sm remove-item-btn"
data-index="0"
data-field-name="pets"
title="Remove this item">
<i class="bi bi-trash"></i>
</button>
</div>
<div class="collapse collapse show" id="pets_item_0_content">
<div class="card-body"><div class="row">
<div class="col-lg-4 col-md-6">
<div class="mb-3"><label for="pets[0].name"><i class="bi bi-heart"></i> Pet's Name</label>
<input name="pets[0].name" id="pets[0].name" class="form-control" value="Max" minlength="1" maxlength="11" placeholder="Enter your pet's name" pattern="\d{3}-\d{2}-\d{4}" inputmode="numeric" autocomplete="off" type="text" />
<div class="form-text">The name of your pet</div></div>
</div>
<div class="col-lg-4 col-md-6">
<div class="mb-3"><div class="mb-3">
<label for="pets[0].species" class="form-label">Species</label>
<div class="input-group">
<span class="input-group-text"><i class="bi bi-bi bi-collection"></i></span>
<select name="pets[0].species" id="pets[0].species" class="form-select"><option value="Dog" selected>Dog 🐕</option>
<option value="Cat">Cat 🐱</option>
<option value="Bird">Bird 🐦</option>
<option value="Fish">Fish 🐠</option>
<option value="Rabbit">Rabbit 🐰</option>
<option value="Hamster">Hamster 🐹</option>
<option value="Reptile">Reptile 🦎</option>
<option value="Other">Other 🐾</option></select>
</div>
<div class="form-text">What type of animal is your pet?</div>
</div></div>
</div>
<div class="col-lg-4 col-md-6">
<div class="mb-3"><label for="pets[0].age"><i class="bi bi-calendar"></i> Age</label>
<input name="pets[0].age" id="pets[0].age" class="form-control" value="3" placeholder="Pet's age in years%" min="0" max="100" step="0.1" inputmode="numeric" type="number" />
<div class="form-text">How old is your pet? (optional)</div></div>
</div>
<div class="col-lg-4 col-md-6">
<div class="mb-3"><label for="pets[0].weight"><i class="bi bi-speedometer2"></i> Weight (lbs)</label>
<input name="pets[0].weight" id="pets[0].weight" class="form-control" value="65.5" placeholder="Pet's weight%" min="0" max="100" step="0.1" inputmode="numeric" type="number" />
<div class="form-text">Weight in pounds (optional - enter 0.01 for tiny pets like birds)</div></div>
</div>
<div class="col-lg-4 col-md-6">
<div class="mb-3"><label for="pets[0].is_vaccinated"><i class="bi bi-shield-check"></i> Vaccinated</label>
<input name="pets[0].is_vaccinated" id="pets[0].is_vaccinated" class="form-check-input" value="1" type="checkbox" />
<div class="form-text">Is your pet up to date with vaccinations?</div></div>
</div>
<div class="col-lg-4 col-md-6">
<div class="mb-3"><label for="pets[0].microchipped"><i class="bi bi-cpu"></i> Microchipped</label>
<input name="pets[0].microchipped" id="pets[0].microchipped" class="form-check-input" checked="checked" value="1" type="checkbox" />
<div class="form-text">Does your pet have a microchip?</div></div>
</div>
<div class="col-lg-4 col-md-6">
<div class="mb-3"><label for="pets[0].breed"><i class="bi bi-award"></i> Breed</label>
<input name="pets[0].breed" id="pets[0].breed" class="form-control" value="Golden Retriever" placeholder="e.g., Golden Retriever, Persian Cat" pattern="\d{3}-\d{2}-\d{4}" maxlength="11" inputmode="numeric" autocomplete="off" type="text" />
<div class="form-text">Specific breed of your pet (optional)</div></div>
</div>
<div class="col-lg-4 col-md-6">
<div class="mb-3"><label for="pets[0].color"><i class="bi bi-palette"></i> Primary Color</label>
<div class="color-input-group"><input name="pets[0].color" id="pets[0].color" class="form-control" value="#000000" type="color" />
<div class="color-value-display" style="display: inline-flex; align-items: center; margin-left: 10px;">
<span id="pets[0].color_value" style="font-family: monospace;">#000000</span>
<div id="pets[0].color_swatch" style="width: 20px; height: 20px; background-color: #000000; border: 1px solid #ccc; margin-left: 5px;"></div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const colorInput = document.getElementById('pets[0].color');
const valueSpan = document.getElementById('pets[0].color_value');
const swatch = document.getElementById('pets[0].color_swatch');
if (colorInput && valueSpan && swatch) {
colorInput.addEventListener('input', function() {
valueSpan.textContent = this.value;
swatch.style.backgroundColor = this.value;
});
}
});
</script>
</div>
<div class="form-text">Primary color of your pet (optional)</div></div>
</div>
<div class="col-lg-4 col-md-6">
<div class="mb-3"><label for="pets[0].last_vet_visit"><i class="bi bi-calendar-date"></i> Last Vet Visit</label>
<div class="birthdate-input-group"><input name="pets[0].last_vet_visit" id="pets[0].last_vet_visit" class="form-control" value="2026-01-03" max="2026-01-07" min="1876-01-01" type="date" />
<div class="age-display" id="pets[0].last_vet_visit_age" style="margin-top: 5px; font-size: 0.9em; color: #666;"></div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const birthdateField = document.getElementById('pets[0].last_vet_visit');
const ageDisplay = document.getElementById('pets[0].last_vet_visit_age');
function calculateAge() {
if (birthdateField.value && ageDisplay) {
const birthDate = new Date(birthdateField.value);
const today = new Date();
let age = today.getFullYear() - birthDate.getFullYear();
const monthDiff = today.getMonth() - birthDate.getMonth();
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {
age--;
}
ageDisplay.textContent = age >= 0 ? `Age: ${age} years` : '';
}
}
if (birthdateField) {
birthdateField.addEventListener('change', calculateAge);
calculateAge(); // Calculate on load if value is set
}
});
</script>
</div>
<div class="form-text">When was the last veterinary checkup? (optional)</div></div>
</div>
<div class="col-lg-4 col-md-6">
<div class="mb-3"><label for="pets[0].special_needs"><i class="bi bi-heart-pulse"></i> Special Needs</label>
<textarea name="pets[0].special_needs" id="pets[0].special_needs" class="form-control" placeholder="Any special care requirements, medications, allergies, etc.">Allergic to chicken-based foods</textarea>
<div class="form-text">Describe any special care requirements (optional)</div></div>
</div></div>
</div>
</div>
</div>
<div class="model-list-item card border mb-3"
data-index="1"
data-title-template="Pet #{index}: {name}"
data-field-name="pets">
<div class="card-header d-flex justify-content-between align-items-center">
<h6 class="mb-0">
<button class="btn btn-link text-decoration-none p-0 text-start"
type="button"
data-bs-toggle="collapse"
data-bs-target="#pets_item_1_content"
aria-expanded="false"
aria-controls="pets_item_1_content">
<i class="bi bi-chevron-right me-2"></i>
<i class="bi bi-card-list me-2"></i>
Pet #2: Luna
</button>
</h6>
<button type="button"
class="btn btn-outline-danger btn-sm remove-item-btn"
data-index="1"
data-field-name="pets"
title="Remove this item">
<i class="bi bi-trash"></i>
</button>
</div>
<div class="collapse collapse show" id="pets_item_1_content">
<div class="card-body"><div class="row">
<div class="col-lg-4 col-md-6">
<div class="mb-3"><label for="pets[1].name"><i class="bi bi-heart"></i> Pet's Name</label>
<input name="pets[1].name" id="pets[1].name" class="form-control" value="Luna" minlength="1" maxlength="11" placeholder="Enter your pet's name" pattern="\d{3}-\d{2}-\d{4}" inputmode="numeric" autocomplete="off" type="text" />
<div class="form-text">The name of your pet</div></div>
</div>
<div class="col-lg-4 col-md-6">
<div class="mb-3"><div class="mb-3">
<label for="pets[1].species" class="form-label">Species</label>
<div class="input-group">
<span class="input-group-text"><i class="bi bi-bi bi-collection"></i></span>
<select name="pets[1].species" id="pets[1].species" class="form-select"><option value="Dog" selected>Dog 🐕</option>
<option value="Cat">Cat 🐱</option>
<option value="Bird">Bird 🐦</option>
<option value="Fish">Fish 🐠</option>
<option value="Rabbit">Rabbit 🐰</option>
<option value="Hamster">Hamster 🐹</option>
<option value="Reptile">Reptile 🦎</option>
<option value="Other">Other 🐾</option></select>
</div>
<div class="form-text">What type of animal is your pet?</div>
</div></div>
</div>
<div class="col-lg-4 col-md-6">
<div class="mb-3"><label for="pets[1].age"><i class="bi bi-calendar"></i> Age</label>
<input name="pets[1].age" id="pets[1].age" class="form-control" value="2" placeholder="Pet's age in years%" min="0" max="100" step="0.1" inputmode="numeric" type="number" />
<div class="form-text">How old is your pet? (optional)</div></div>
</div>
<div class="col-lg-4 col-md-6">
<div class="mb-3"><label for="pets[1].weight"><i class="bi bi-speedometer2"></i> Weight (lbs)</label>
<input name="pets[1].weight" id="pets[1].weight" class="form-control" value="8.5" placeholder="Pet's weight%" min="0" max="100" step="0.1" inputmode="numeric" type="number" />
<div class="form-text">Weight in pounds (optional - enter 0.01 for tiny pets like birds)</div></div>
</div>
<div class="col-lg-4 col-md-6">
<div class="mb-3"><label for="pets[1].is_vaccinated"><i class="bi bi-shield-check"></i> Vaccinated</label>
<input name="pets[1].is_vaccinated" id="pets[1].is_vaccinated" class="form-check-input" value="1" type="checkbox" />
<div class="form-text">Is your pet up to date with vaccinations?</div></div>
</div>
<div class="col-lg-4 col-md-6">
<div class="mb-3"><label for="pets[1].microchipped"><i class="bi bi-cpu"></i> Microchipped</label>
<input name="pets[1].microchipped" id="pets[1].microchipped" class="form-check-input" checked="checked" value="1" type="checkbox" />
<div class="form-text">Does your pet have a microchip?</div></div>
</div>
<div class="col-lg-4 col-md-6">
<div class="mb-3"><label for="pets[1].breed"><i class="bi bi-award"></i> Breed</label>
<input name="pets[1].breed" id="pets[1].breed" class="form-control" value="Siamese" placeholder="e.g., Golden Retriever, Persian Cat" pattern="\d{3}-\d{2}-\d{4}" maxlength="11" inputmode="numeric" autocomplete="off" type="text" />
<div class="form-text">Specific breed of your pet (optional)</div></div>
</div>
<div class="col-lg-4 col-md-6">
<div class="mb-3"><label for="pets[1].color"><i class="bi bi-palette"></i> Primary Color</label>
<div class="color-input-group"><input name="pets[1].color" id="pets[1].color" class="form-control" value="#000000" type="color" />
<div class="color-value-display" style="display: inline-flex; align-items: center; margin-left: 10px;">
<span id="pets[1].color_value" style="font-family: monospace;">#000000</span>
<div id="pets[1].color_swatch" style="width: 20px; height: 20px; background-color: #000000; border: 1px solid #ccc; margin-left: 5px;"></div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const colorInput = document.getElementById('pets[1].color');
const valueSpan = document.getElementById('pets[1].color_value');
const swatch = document.getElementById('pets[1].color_swatch');
if (colorInput && valueSpan && swatch) {
colorInput.addEventListener('input', function() {
valueSpan.textContent = this.value;
swatch.style.backgroundColor = this.value;
});
}
});
</script>
</div>
<div class="form-text">Primary color of your pet (optional)</div></div>
</div>
<div class="col-lg-4 col-md-6">
<div class="mb-3"><label for="pets[1].last_vet_visit"><i class="bi bi-calendar-date"></i> Last Vet Visit</label>
<div class="birthdate-input-group"><input name="pets[1].last_vet_visit" id="pets[1].last_vet_visit" class="form-control" value="2026-01-04" max="2026-01-07" min="1876-01-01" type="date" />
<div class="age-display" id="pets[1].last_vet_visit_age" style="margin-top: 5px; font-size: 0.9em; color: #666;"></div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const birthdateField = document.getElementById('pets[1].last_vet_visit');
const ageDisplay = document.getElementById('pets[1].last_vet_visit_age');
function calculateAge() {
if (birthdateField.value && ageDisplay) {
const birthDate = new Date(birthdateField.value);
const today = new Date();
let age = today.getFullYear() - birthDate.getFullYear();
const monthDiff = today.getMonth() - birthDate.getMonth();
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {
age--;
}
ageDisplay.textContent = age >= 0 ? `Age: ${age} years` : '';
}
}
if (birthdateField) {
birthdateField.addEventListener('change', calculateAge);
calculateAge(); // Calculate on load if value is set
}
});
</script>
</div>
<div class="form-text">When was the last veterinary checkup? (optional)</div></div>
</div>
<div class="col-lg-4 col-md-6">
<div class="mb-3"><label for="pets[1].special_needs"><i class="bi bi-heart-pulse"></i> Special Needs</label>
<textarea name="pets[1].special_needs" id="pets[1].special_needs" class="form-control" placeholder="Any special care requirements, medications, allergies, etc.">Indoor only, needs daily medication for thyroid</textarea>
<div class="form-text">Describe any special care requirements (optional)</div></div>
</div></div>
</div>
</div>
</div></div>
<div class="model-list-controls mt-2">
<button type="button" class="btn btn-outline-primary btn-sm add-item-btn" data-target="pets">
<i class="bi bi-plus-circle"></i> Add Item
</button>
</div>
</div>
<div class="form-text text-muted model-list-help">Add information about each of your pets</div>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
<script>
(function() {
document.addEventListener('DOMContentLoaded', function() {
// Prevent Enter key from submitting forms unless on submit button
const forms = document.querySelectorAll('form.pydantic-form');
forms.forEach(function(form) {
form.addEventListener('keydown', function(e) {
// Check if Enter key is pressed
if (e.key === 'Enter' || e.keyCode === 13) {
const target = e.target;
// Allow Enter in textareas (for multi-line input)
if (target.tagName === 'TEXTAREA') {
return;
}
// Allow Enter on submit buttons
if (target.tagName === 'BUTTON' && target.type === 'submit') {
return;
}
// Allow Enter on input type="submit"
if (target.tagName === 'INPUT' && target.type === 'submit') {
return;
}
// Prevent form submission for all other cases
e.preventDefault();
return false;
}
});
});
});
})();
</script>
<script>
(function() {
'use strict';
// Ensure this runs after DOM is loaded
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initializeModelLists);
} else {
initializeModelLists();
}
function initializeModelLists() {
// Add item functionality
const addButtons = document.querySelectorAll('.add-item-btn');
addButtons.forEach(button => {
if (!button.hasAttribute('data-initialized')) {
button.setAttribute('data-initialized', 'true');
button.addEventListener('click', handleAddItem);
}
});
// Remove item functionality - use direct event listeners
const removeButtons = document.querySelectorAll('.remove-item-btn');
removeButtons.forEach(button => {
if (!button.hasAttribute('data-initialized')) {
button.setAttribute('data-initialized', 'true');
button.addEventListener('click', handleRemoveItem);
}
});
// Also set up delegation for dynamically added buttons
document.addEventListener('click', function(e) {
if (e.target.closest('.remove-item-btn') && !e.target.closest('.remove-item-btn').hasAttribute('data-initialized')) {
const button = e.target.closest('.remove-item-btn');
button.setAttribute('data-initialized', 'true');
handleRemoveItem.call(button, e);
}
});
}
function handleAddItem(e) {
e.preventDefault();
e.stopPropagation();
const fieldName = this.dataset.target;
const container = document.querySelector(`[data-field-name="${fieldName}"]`);
if (!container) return;
const itemsContainer = container.querySelector('.model-list-items');
const maxItems = parseInt(container.dataset.maxItems || '10');
const currentItems = itemsContainer.querySelectorAll('.model-list-item').length;
if (currentItems >= maxItems) {
alert(`Maximum ${maxItems} items allowed.`);
return;
}
addNewListItem(fieldName, currentItems);
updateItemIndices(itemsContainer);
}
function handleRemoveItem(e) {
e.preventDefault();
e.stopPropagation();
const button = e.currentTarget || this;
const item = button.closest('.model-list-item');
if (!item) return;
const container = item.closest('.model-list-container');
if (!container) return;
const minItems = parseInt(container.dataset.minItems || '0');
const itemsContainer = container.querySelector('.model-list-items');
const currentItems = itemsContainer.querySelectorAll('.model-list-item').length;
if (currentItems <= minItems) {
alert(`Minimum ${minItems} items required.`);
return;
}
// Check if item has data
const hasData = Array.from(item.querySelectorAll('input, select, textarea')).some(input => {
if (input.type === 'checkbox' || input.type === 'radio') {
return input.checked;
}
return input.value && input.value.trim() !== '';
});
if (hasData) {
if (!confirm('Are you sure you want to remove this item? All data will be lost.')) {
return;
}
}
item.remove();
updateItemIndices(itemsContainer);
}
// Update titles when input fields change
document.addEventListener('input', function(e) {
if (e.target.name && (e.target.name.includes('.name') || e.target.name.includes('.relationship'))) {
updateItemTitle(e.target);
}
});
// Handle collapse icons
document.addEventListener('click', function(e) {
const collapseButton = e.target.closest('[data-bs-toggle="collapse"]');
if (collapseButton) {
const icon = collapseButton.querySelector('.bi-chevron-down, .bi-chevron-right');
if (icon) {
setTimeout(() => {
const isExpanded = collapseButton.getAttribute('aria-expanded') === 'true';
icon.className = isExpanded ? 'bi bi-chevron-down me-2' : 'bi bi-chevron-right me-2';
}, 50);
}
}
});
})();
function addNewListItem(fieldName, index) {
const container = document.querySelector(`[data-field-name="${fieldName}"]`);
const itemsContainer = container.querySelector('.model-list-items');
// Get the first item as a template if it exists
const firstItem = itemsContainer.querySelector('.model-list-item');
if (firstItem) {
const newItem = firstItem.cloneNode(true);
// Clear all input values
newItem.querySelectorAll('input, select, textarea').forEach(input => {
if (input.type === 'checkbox' || input.type === 'radio') {
input.checked = false;
} else {
input.value = '';
}
});
// Update data-index
newItem.dataset.index = index;
// Update field names and IDs
updateFieldNames(newItem, fieldName, index);
// Update collapse IDs
updateCollapseIds(newItem, fieldName, index);
// Expand the new item
const collapseDiv = newItem.querySelector('.collapse');
if (collapseDiv) {
collapseDiv.classList.add('show');
}
// Update collapse button aria-expanded
const collapseButton = newItem.querySelector('[data-bs-toggle="collapse"]');
if (collapseButton) {
collapseButton.setAttribute('aria-expanded', 'true');
const icon = collapseButton.querySelector('.bi-chevron-down, .bi-chevron-right');
if (icon) {
icon.className = 'bi bi-chevron-down me-2';
}
}
itemsContainer.appendChild(newItem);
}
}
function updateItemIndices(container) {
const items = container.querySelectorAll('.model-list-item');
items.forEach((item, index) => {
item.dataset.index = index;
// Update field names first
const fieldName = container.closest('.model-list-container').dataset.fieldName;
updateFieldNames(item, fieldName, index);
updateCollapseIds(item, fieldName, index);
// Update title using the dynamic template
updateItemTitleFromData(item, index);
});
}
function updateFieldNames(item, fieldName, index) {
item.querySelectorAll('input, select, textarea').forEach(input => {
if (input.name) {
// Update name attribute to use correct index
input.name = input.name.replace(/\[\d+\]/, `[${index}]`);
}
if (input.id) {
// Update id attribute
input.id = input.id.replace(/\[\d+\]/, `[${index}]`);
}
});
item.querySelectorAll('label').forEach(label => {
if (label.getAttribute('for')) {
label.setAttribute('for', label.getAttribute('for').replace(/\[\d+\]/, `[${index}]`));
}
});
}
function updateCollapseIds(item, fieldName, index) {
const collapseDiv = item.querySelector('.collapse');
const collapseButton = item.querySelector('[data-bs-toggle="collapse"]');
if (collapseDiv && collapseButton) {
const newId = `${fieldName}_item_${index}_content`;
collapseDiv.id = newId;
collapseButton.setAttribute('data-bs-target', `#${newId}`);
collapseButton.setAttribute('aria-controls', newId);
}
}
function updateItemTitle(inputElement) {
const item = inputElement.closest('.model-list-item');
if (!item) return;
updateItemTitleFromData(item);
}
function updateItemTitleFromData(item, forceIndex = null) {
const index = forceIndex !== null ? forceIndex : parseInt(item.dataset.index);
const titleTemplate = item.dataset.titleTemplate || 'Item #{index}';
const titleElement = item.querySelector('h6 button, h6 span');
if (!titleElement) return;
// Extract current form data from the item
const formData = { index: index + 1 };
item.querySelectorAll('input, select, textarea').forEach(input => {
if (input.name) {
// Extract field name (e.g., "pets[0].name" -> "name")
const fieldMatch = input.name.match(/\.([^.]+)$/);
if (fieldMatch) {
const fieldName = fieldMatch[1];
if (input.type === 'checkbox') {
formData[fieldName] = input.checked;
} else {
formData[fieldName] = input.value || '';
}
}
}
});
// Generate title from template
let newTitle;
try {
newTitle = titleTemplate.replace(/\{([^}]+)\}/g, (match, key) => {
return formData[key] || '';
});
} catch (e) {
newTitle = `Item #${index + 1}`;
}
// Update the title while preserving icons
const cardIcon = '<i class="bi bi-card-list me-2"></i>';
if (titleElement.tagName === 'BUTTON') {
const chevronIcon = titleElement.querySelector('.bi-chevron-down, .bi-chevron-right');
const chevronHtml = chevronIcon ? chevronIcon.outerHTML : '<i class="bi bi-chevron-down me-2"></i>';
titleElement.innerHTML = `${chevronHtml}${cardIcon}${newTitle}`;
} else {
titleElement.innerHTML = `${cardIcon}${newTitle}`;
}
}
</script>
class PetRegistrationForm(FormModel):
"""Complete pet registration form with owner information and pets list."""
owner_name: str = FormField(
title="Owner Name",
input_type="text",
placeholder="Enter your full name",
help_text="Your full name as the pet owner",
icon="person",
min_length=2,
max_length=100
)
email: EmailStr = FormField(
title="Email Address",
input_type="email",
placeholder="your.email@example.com",
help_text="Your contact email address",
icon="envelope"
)
address: Optional[str] = FormField(
None,
title="Address",
input_type="textarea",
placeholder="Enter your full address...",
help_text="Your home address (optional)",
icon="house",
max_length=500
)
emergency_contact: Optional[str] = FormField(
None,
title="Emergency Contact",
input_type="text",
placeholder="Emergency contact name and phone",
help_text="Someone to contact in case of emergency",
icon="person-exclamation",
max_length=100
)
pets: List[PetModel] = FormField(
default_factory=list,
title="Your Pets",
input_type="model_list",
help_text="Add information about each of your pets",
icon="heart",
min_length=1,
max_length=10,
model_class=PetModel,
add_button_text="Add Another Pet",
remove_button_text="Remove Pet",
collapsible_items=True,
items_expanded=False,
item_title_template="Pet #{index}: {name}",
section_design={
"section_title": "Pet Registry",
"section_description": "Register each of your beloved pets with detailed information",
"icon": "bi bi-heart-fill",
"collapsible": True,
"collapsed": False
}
)
{
"$defs": {
"PetModel": {
"description": "Enhanced pet information model showcasing various input types.",
"properties": {
"name": {
"autofocus": false,
"description": "The name of your pet",
"disabled": false,
"help_text": "The name of your pet",
"icon": "heart",
"input_type": "text",
"maxLength": 50,
"minLength": 1,
"placeholder": "Enter your pet's name",
"readonly": false,
"title": "Pet's Name",
"type": "string"
},
"species": {
"autofocus": false,
"description": "What type of animal is your pet?",
"disabled": false,
"help_text": "What type of animal is your pet?",
"icon": "collection",
"input_type": "select",
"options": [
{
"label": "Dog \ud83d\udc15",
"value": "Dog"
},
{
"label": "Cat \ud83d\udc31",
"value": "Cat"
},
{
"label": "Bird \ud83d\udc26",
"value": "Bird"
},
{
"label": "Fish \ud83d\udc20",
"value": "Fish"
},
{
"label": "Rabbit \ud83d\udc30",
"value": "Rabbit"
},
{
"label": "Hamster \ud83d\udc39",
"value": "Hamster"
},
{
"label": "Reptile \ud83e\udd8e",
"value": "Reptile"
},
{
"label": "Other \ud83d\udc3e",
"value": "Other"
}
],
"readonly": false,
"title": "Species",
"type": "string"
},
"age": {
"anyOf": [
{
"maximum": 50,
"minimum": 0,
"type": "integer"
},
{
"type": "null"
}
],
"autofocus": false,
"default": null,
"description": "How old is your pet? (optional)",
"disabled": false,
"help_text": "How old is your pet? (optional)",
"icon": "calendar",
"input_type": "number",
"placeholder": "Pet's age in years",
"readonly": false,
"title": "Age"
},
"weight": {
"anyOf": [
{
"maximum": 500.0,
"minimum": 0.01,
"type": "number"
},
{
"type": "null"
}
],
"autofocus": false,
"default": null,
"description": "Weight in pounds (optional - enter 0.01 for tiny pets like birds)",
"disabled": false,
"help_text": "Weight in pounds (optional - enter 0.01 for tiny pets like birds)",
"icon": "speedometer2",
"input_type": "number",
"placeholder": "Pet's weight",
"readonly": false,
"title": "Weight (lbs)"
},
"is_vaccinated": {
"autofocus": false,
"default": false,
"description": "Is your pet up to date with vaccinations?",
"disabled": false,
"help_text": "Is your pet up to date with vaccinations?",
"icon": "shield-check",
"input_type": "checkbox",
"readonly": false,
"title": "Vaccinated",
"type": "boolean"
},
"microchipped": {
"autofocus": false,
"default": false,
"description": "Does your pet have a microchip?",
"disabled": false,
"help_text": "Does your pet have a microchip?",
"icon": "cpu",
"input_type": "checkbox",
"readonly": false,
"title": "Microchipped",
"type": "boolean"
},
"breed": {
"anyOf": [
{
"maxLength": 100,
"type": "string"
},
{
"type": "null"
}
],
"autofocus": false,
"default": null,
"description": "Specific breed of your pet (optional)",
"disabled": false,
"help_text": "Specific breed of your pet (optional)",
"icon": "award",
"input_type": "text",
"placeholder": "e.g., Golden Retriever, Persian Cat",
"readonly": false,
"title": "Breed"
},
"color": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"autofocus": false,
"default": null,
"description": "Primary color of your pet (optional)",
"disabled": false,
"help_text": "Primary color of your pet (optional)",
"icon": "palette",
"input_type": "color",
"readonly": false,
"title": "Primary Color"
},
"last_vet_visit": {
"anyOf": [
{
"format": "date",
"type": "string"
},
{
"type": "null"
}
],
"autofocus": false,
"default": null,
"description": "When was the last veterinary checkup? (optional)",
"disabled": false,
"help_text": "When was the last veterinary checkup? (optional)",
"icon": "calendar-date",
"input_type": "date",
"readonly": false,
"title": "Last Vet Visit"
},
"special_needs": {
"anyOf": [
{
"maxLength": 500,
"type": "string"
},
{
"type": "null"
}
],
"autofocus": false,
"default": null,
"description": "Describe any special care requirements (optional)",
"disabled": false,
"help_text": "Describe any special care requirements (optional)",
"icon": "heart-pulse",
"input_type": "textarea",
"placeholder": "Any special care requirements, medications, allergies, etc.",
"readonly": false,
"title": "Special Needs"
}
},
"required": [
"name",
"species"
],
"title": "PetModel",
"type": "object"
}
},
"description": "Complete pet registration form with owner information and pets list.",
"properties": {
"owner_name": {
"autofocus": false,
"description": "Your full name as the pet owner",
"disabled": false,
"help_text": "Your full name as the pet owner",
"icon": "person",
"input_type": "text",
"maxLength": 100,
"minLength": 2,
"placeholder": "Enter your full name",
"readonly": false,
"title": "Owner Name",
"type": "string"
},
"email": {
"autofocus": false,
"description": "Your contact email address",
"disabled": false,
"format": "email",
"help_text": "Your contact email address",
"icon": "envelope",
"input_type": "email",
"placeholder": "your.email@example.com",
"readonly": false,
"title": "Email Address",
"type": "string"
},
"address": {
"anyOf": [
{
"maxLength": 500,
"type": "string"
},
{
"type": "null"
}
],
"autofocus": false,
"default": null,
"description": "Your home address (optional)",
"disabled": false,
"help_text": "Your home address (optional)",
"icon": "house",
"input_type": "textarea",
"placeholder": "Enter your full address...",
"readonly": false,
"title": "Address"
},
"emergency_contact": {
"anyOf": [
{
"maxLength": 100,
"type": "string"
},
{
"type": "null"
}
],
"autofocus": false,
"default": null,
"description": "Someone to contact in case of emergency",
"disabled": false,
"help_text": "Someone to contact in case of emergency",
"icon": "person-exclamation",
"input_type": "text",
"placeholder": "Emergency contact name and phone",
"readonly": false,
"title": "Emergency Contact"
},
"pets": {
"add_button_text": "Add Another Pet",
"autofocus": false,
"collapsible_items": true,
"default_factory": "builtins.list",
"description": "Add information about each of your pets",
"disabled": false,
"help_text": "Add information about each of your pets",
"icon": "heart",
"input_type": "model_list",
"item_title_template": "Pet #{index}: {name}",
"items": {
"$ref": "#/$defs/PetModel"
},
"items_expanded": false,
"maxItems": 10,
"minItems": 1,
"model_class": "src.models.PetModel",
"readonly": false,
"remove_button_text": "Remove Pet",
"section_design": {
"collapsed": false,
"collapsible": true,
"icon": "bi bi-heart-fill",
"section_description": "Register each of your beloved pets with detailed information",
"section_title": "Pet Registry"
},
"title": "Your Pets",
"type": "array"
}
},
"required": [
"owner_name",
"email",
"pets"
],
"title": "PetRegistrationForm",
"type": "object"
}{
"owner_name": {
"required": true,
"type": "string",
"minLength": 2,
"maxLength": 100
},
"email": {
"required": true,
"type": "string",
"format": "email"
},
"address": {
"required": false
},
"emergency_contact": {
"required": false
},
"pets": {
"required": true,
"type": "array"
}
}{
"errors": {},
"data": {
"owner_name": "Sarah Mitchell",
"email": "sarah.mitchell@example.com",
"address": "123 Main Street\r\nApt 4B\r\nSpringfield, IL 62701",
"emergency_contact": "John Mitchell - (555) 123-4567",
"pets": [
{
"name": "Max",
"species": "Dog",
"age": 3,
"weight": 65.5,
"is_vaccinated": false,
"microchipped": true,
"breed": "Golden Retriever",
"color": "#000000",
"last_vet_visit": "2026-01-03",
"special_needs": "Allergic to chicken-based foods"
},
{
"name": "Luna",
"species": "Dog",
"age": 2,
"weight": 8.5,
"is_vaccinated": false,
"microchipped": true,
"breed": "Siamese",
"color": "#000000",
"last_vet_visit": "2026-01-04",
"special_needs": "Indoor only, needs daily medication for thyroid"
}
]
}
}