Form Component Overview
Form Component
Section titled “Form Component”The <form-component> is a dynamic form renderer that creates forms based on a spec schema. It supports validation, nested fields, dynamic updates, and external control through browser hooks.
Registration
Section titled “Registration”The component is registered as a Custom Element:
import { defineCustomElement } from 'vue';import FormCe from '@/form/view/Form.ce.vue';import { useCore } from './store/core';
// Expose hooks to browser contextwindow.$useFormCore = useCore();
// Register custom elementcustomElements.define('form-component', defineCustomElement(FormCe));Features
Section titled “Features”- Spec-Driven: Forms are defined by JSON schemas
- Validation: Built-in validation using Yup schemas
- Nested Fields: Support for collections, arrays, and dynamic nested structures
- Theming: Light and dark mode support
- External Control: Hooks for subscribing to changes and modifying form structure
- Action Buttons: Custom actions on fields (e.g., “Add Connection”)
Basic Usage
Section titled “Basic Usage”<script setup lang="ts">import { ref } from 'vue';
const formId = 'contact-form';
const formSpec = { type: 'collection', name: 'contact', spec: [ { type: 'text', name: 'firstName', label: 'First Name', required: true, }, { type: 'text', name: 'lastName', label: 'Last Name', required: true, }, { type: 'email', name: 'email', label: 'Email Address', required: true, }, { type: 'text', name: 'message', label: 'Message', multiline: true, }, ],};
const initialValues = ref({ firstName: '', lastName: '', email: '', message: '',});
function handleSubmit(data: unknown) { console.log('Form submitted:', data);}
function handleChange(data: unknown) { console.log('Form changed:', data);}</script>
<template> <form-component :id="formId" theme="light" :spec="formSpec" :values="initialValues" submit-button-text="Send Message" @submit="handleSubmit" @change="handleChange" /></template><!DOCTYPE html><html><head> <script src="./dist/form.js"></script></head><body> <form-component id="contact-form" theme="light" submit-button-text="Send Message" ></form-component>
<script> const form = document.querySelector('form-component');
// Set spec as JSON string or object form.spec = JSON.stringify({ type: 'collection', name: 'contact', spec: [ { type: 'text', name: 'firstName', label: 'First Name', required: true }, { type: 'text', name: 'lastName', label: 'Last Name', required: true }, { type: 'email', name: 'email', label: 'Email', required: true }, ] });
// Listen for events form.addEventListener('submit', (e) => { console.log('Submitted:', e.detail); });
form.addEventListener('change', (e) => { console.log('Changed:', e.detail); }); </script></body></html>Select Fields with Nested Options
Section titled “Select Fields with Nested Options”Selects can define nested fields that appear when an option is selected:
const spec = { type: 'collection', spec: [ { type: 'select', name: 'paymentMethod', label: 'Payment Method', options: [ { label: 'Credit Card', value: 'credit_card', nested: [ { type: 'text', name: 'cardNumber', label: 'Card Number', required: true }, { type: 'text', name: 'expiry', label: 'MM/YY', required: true }, { type: 'text', name: 'cvv', label: 'CVV', required: true }, ], }, { label: 'PayPal', value: 'paypal', nested: [ { type: 'email', name: 'paypalEmail', label: 'PayPal Email', required: true }, ], }, { label: 'Bank Transfer', value: 'bank', // No nested fields }, ], }, ],};Array Fields
Section titled “Array Fields”Create repeatable field groups:
const spec = { type: 'collection', spec: [ { type: 'array', name: 'contacts', label: 'Emergency Contacts', spec: { type: 'collection', spec: [ { type: 'text', name: 'name', label: 'Name', required: true }, { type: 'tel', name: 'phone', label: 'Phone', required: true }, { type: 'text', name: 'relationship', label: 'Relationship' }, ], }, }, ],};Field Actions
Section titled “Field Actions”Fields can have action buttons for triggering external behavior:
const spec = { type: 'collection', spec: [ { type: 'select', name: 'integration', label: 'Integration', options: [], action: { label: 'Add New', value: 'add.integration', }, }, ],};
// Listen for the actionform.addEventListener('action', (e) => { const [action, value] = e.detail; if (action.value === 'add.integration') { // Open integration creation dialog openIntegrationDialog(); }});Advanced Options
Section titled “Advanced Options”Fields can be marked as “advanced” and hidden by default:
const spec = { type: 'collection', spec: [ { type: 'text', name: 'name', label: 'Name', required: true }, { type: 'text', name: 'description', label: 'Description' }, // Advanced options (hidden by default) { type: 'number', name: 'timeout', label: 'Timeout (ms)', advanced: true }, { type: 'boolean', name: 'debug', label: 'Debug Mode', advanced: true }, ],};Internal Architecture
Section titled “Internal Architecture”src/form/├── form.ts # Custom element registration├── store/│ ├── core.ts # Browser hooks (window.$useFormCore)│ └── workspace.ts # Form instance management├── view/│ └── Form.ce.vue # Main component└── types/ └── index.ts # Type definitionsNext Steps
Section titled “Next Steps”- Props & Events - Complete API reference
- Hooks API - External control with browser hooks
- Spec Paradigm - Deep dive into schema definitions