763 lines
17 KiB
Markdown
763 lines
17 KiB
Markdown
# Form Component Documentation
|
|
|
|
## Overview
|
|
|
|
A highly flexible and configurable dynamic form component built with Vuetify. This component generates forms based on field configuration objects and supports various input types, validation, responsive layouts, and both controlled and uncontrolled form state management.
|
|
|
|
## Basic Usage
|
|
|
|
```vue
|
|
<template>
|
|
<Form
|
|
:fields="formFields"
|
|
:form-data="formData"
|
|
@submit="handleSubmit"
|
|
@change="handleFieldChange"
|
|
/>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref } from 'vue'
|
|
import Form from './components/common/Form.vue'
|
|
|
|
const formData = ref({})
|
|
|
|
const formFields = [
|
|
{
|
|
name: 'firstName',
|
|
label: 'First Name',
|
|
type: 'text',
|
|
required: true,
|
|
cols: 12,
|
|
md: 6
|
|
},
|
|
{
|
|
name: 'email',
|
|
label: 'Email',
|
|
type: 'text',
|
|
format: 'email',
|
|
required: true,
|
|
cols: 12,
|
|
md: 6
|
|
},
|
|
{
|
|
name: 'bio',
|
|
label: 'Biography',
|
|
type: 'textarea',
|
|
rows: 4,
|
|
cols: 12
|
|
}
|
|
]
|
|
|
|
const handleSubmit = (data) => {
|
|
console.log('Form submitted:', data)
|
|
}
|
|
|
|
const handleFieldChange = (event) => {
|
|
console.log('Field changed:', event.fieldName, event.value)
|
|
}
|
|
</script>
|
|
```
|
|
|
|
## Props
|
|
|
|
### `fields` (Array) - Required
|
|
- **Description:** Array of field configuration objects that define the form structure
|
|
- **Type:** `Array<Object>`
|
|
- **Required:** `true`
|
|
|
|
### `formData` (Object)
|
|
- **Description:** External form data object for controlled form state
|
|
- **Type:** `Object`
|
|
- **Default:** `null`
|
|
- **Note:** When provided, the form operates in controlled mode. When null, uses internal state.
|
|
|
|
### `onChange` (Function)
|
|
- **Description:** Global change handler function called when any field changes
|
|
- **Type:** `Function`
|
|
- **Signature:** `(fieldName: string, value: any, formData: Object) => void`
|
|
|
|
### `onSubmit` (Function)
|
|
- **Description:** Submit handler function called when form is submitted
|
|
- **Type:** `Function`
|
|
- **Signature:** `(formData: Object) => Promise<void> | void`
|
|
|
|
### `showSubmitButton` (Boolean)
|
|
- **Description:** Controls visibility of the submit button
|
|
- **Type:** `Boolean`
|
|
- **Default:** `true`
|
|
|
|
### `showCancelButton` (Boolean)
|
|
- **Description:** Controls visibility of the cancel button
|
|
- **Type:** `Boolean`
|
|
- **Default:** `false`
|
|
|
|
### `submitButtonText` (String)
|
|
- **Description:** Text displayed on the submit button
|
|
- **Type:** `String`
|
|
- **Default:** `'Submit'`
|
|
|
|
### `cancelButtonText` (String)
|
|
- **Description:** Text displayed on the cancel button
|
|
- **Type:** `String`
|
|
- **Default:** `'Cancel'`
|
|
|
|
### `validateOnChange` (Boolean)
|
|
- **Description:** Enables real-time validation as fields change
|
|
- **Type:** `Boolean`
|
|
- **Default:** `true`
|
|
|
|
## Field Configuration
|
|
|
|
Each field object in the `fields` array supports the following properties:
|
|
|
|
### Basic Properties
|
|
- **`name`** (String, required) - Unique identifier for the field
|
|
- **`label`** (String, required) - Display label for the field
|
|
- **`type`** (String, required) - Field input type
|
|
- **`required`** (Boolean, default: `false`) - Makes the field mandatory
|
|
- **`disabled`** (Boolean, default: `false`) - Disables the field
|
|
- **`readonly`** (Boolean, default: `false`) - Makes the field read-only
|
|
- **`placeholder`** (String) - Placeholder text for input fields
|
|
- **`helpText`** (String) - Help text displayed below the field
|
|
- **`defaultValue`** (Any) - Initial value for the field
|
|
|
|
### Layout Properties
|
|
- **`cols`** (Number, default: `12`) - Column width on extra small screens
|
|
- **`sm`** (Number, default: `12`) - Column width on small screens
|
|
- **`md`** (Number, default: `6`) - Column width on medium screens
|
|
- **`lg`** (Number, default: `6`) - Column width on large screens
|
|
|
|
### Validation Properties
|
|
- **`validate`** (Function) - Custom validation function
|
|
- **Signature:** `(value: any) => string | null`
|
|
- **Returns:** Error message string or null if valid
|
|
|
|
### Field-Specific Properties
|
|
- **`onChangeOverride`** (Function) - Field-specific change handler that overrides global onChange
|
|
- **Signature:** `(value: any, fieldName: string, formData: Object) => void`
|
|
|
|
## Field Types
|
|
|
|
### Text Input (`type: 'text'`)
|
|
Standard text input field with optional format validation.
|
|
|
|
```javascript
|
|
{
|
|
name: 'username',
|
|
label: 'Username',
|
|
type: 'text',
|
|
required: true,
|
|
placeholder: 'Enter your username'
|
|
}
|
|
```
|
|
|
|
**Additional Properties:**
|
|
- **`format`** (String) - Input format validation (`'email'` for email validation)
|
|
|
|
### Number Input (`type: 'number'`)
|
|
Numeric input field with optional min/max constraints.
|
|
|
|
```javascript
|
|
{
|
|
name: 'age',
|
|
label: 'Age',
|
|
type: 'number',
|
|
min: 0,
|
|
max: 120,
|
|
step: 1
|
|
}
|
|
```
|
|
|
|
**Additional Properties:**
|
|
- **`min`** (Number) - Minimum allowed value
|
|
- **`max`** (Number) - Maximum allowed value
|
|
- **`step`** (Number) - Step increment for the input
|
|
|
|
### Textarea (`type: 'textarea'`)
|
|
Multi-line text input for longer content.
|
|
|
|
```javascript
|
|
{
|
|
name: 'description',
|
|
label: 'Description',
|
|
type: 'textarea',
|
|
rows: 4,
|
|
placeholder: 'Enter description...'
|
|
}
|
|
```
|
|
|
|
**Additional Properties:**
|
|
- **`rows`** (Number, default: `3`) - Number of visible text lines
|
|
|
|
### Select Dropdown (`type: 'select'`)
|
|
Dropdown selection field with predefined options.
|
|
|
|
```javascript
|
|
{
|
|
name: 'country',
|
|
label: 'Country',
|
|
type: 'select',
|
|
required: true,
|
|
options: [
|
|
{ label: 'United States', value: 'us' },
|
|
{ label: 'Canada', value: 'ca' },
|
|
{ label: 'Mexico', value: 'mx' }
|
|
]
|
|
}
|
|
```
|
|
|
|
**Additional Properties:**
|
|
- **`options`** (Array, required) - Array of option objects with `label` and `value` properties
|
|
|
|
### Checkbox (`type: 'checkbox'`)
|
|
Boolean checkbox input.
|
|
|
|
```javascript
|
|
{
|
|
name: 'subscribe',
|
|
label: 'Subscribe to newsletter',
|
|
type: 'checkbox',
|
|
defaultValue: false
|
|
}
|
|
```
|
|
|
|
### Radio Group (`type: 'radio'`)
|
|
Radio button group for single selection from multiple options.
|
|
|
|
```javascript
|
|
{
|
|
name: 'gender',
|
|
label: 'Gender',
|
|
type: 'radio',
|
|
required: true,
|
|
options: [
|
|
{ label: 'Male', value: 'male' },
|
|
{ label: 'Female', value: 'female' },
|
|
{ label: 'Other', value: 'other' }
|
|
]
|
|
}
|
|
```
|
|
|
|
**Additional Properties:**
|
|
- **`options`** (Array, required) - Array of option objects with `label` and `value` properties
|
|
|
|
### Date Input (`type: 'date'`)
|
|
Date picker input field.
|
|
|
|
```javascript
|
|
{
|
|
name: 'birthDate',
|
|
label: 'Birth Date',
|
|
type: 'date',
|
|
required: true,
|
|
min: '1900-01-01',
|
|
max: '2025-12-31'
|
|
}
|
|
```
|
|
|
|
**Additional Properties:**
|
|
- **`min`** (String) - Minimum allowed date (YYYY-MM-DD format)
|
|
- **`max`** (String) - Maximum allowed date (YYYY-MM-DD format)
|
|
|
|
### DateTime Input (`type: 'datetime'`)
|
|
Date and time picker input field.
|
|
|
|
```javascript
|
|
{
|
|
name: 'appointmentTime',
|
|
label: 'Appointment Time',
|
|
type: 'datetime',
|
|
required: true
|
|
}
|
|
```
|
|
|
|
**Additional Properties:**
|
|
- **`min`** (String) - Minimum allowed datetime
|
|
- **`max`** (String) - Maximum allowed datetime
|
|
|
|
### File Input (`type: 'file'`)
|
|
File upload input field.
|
|
|
|
```javascript
|
|
{
|
|
name: 'resume',
|
|
label: 'Resume',
|
|
type: 'file',
|
|
accept: '.pdf,.doc,.docx',
|
|
multiple: false
|
|
}
|
|
```
|
|
|
|
**Additional Properties:**
|
|
- **`accept`** (String) - File types to accept (MIME types or file extensions)
|
|
- **`multiple`** (Boolean, default: `false`) - Allow multiple file selection
|
|
|
|
## Events
|
|
|
|
### `update:formData`
|
|
- **Description:** Emitted when form data changes (controlled mode only)
|
|
- **Payload:** Updated form data object
|
|
- **Usage:** `@update:formData="handleFormDataUpdate"`
|
|
|
|
### `submit`
|
|
- **Description:** Emitted when form is successfully submitted
|
|
- **Payload:** Form data object
|
|
- **Usage:** `@submit="handleSubmit"`
|
|
|
|
### `cancel`
|
|
- **Description:** Emitted when cancel button is clicked
|
|
- **Usage:** `@cancel="handleCancel"`
|
|
|
|
### `change`
|
|
- **Description:** Emitted when any field value changes
|
|
- **Payload:** Object with `fieldName`, `value`, and `formData` properties
|
|
- **Usage:** `@change="handleFieldChange"`
|
|
|
|
## Exposed Methods
|
|
|
|
The component exposes several methods that can be accessed via template refs:
|
|
|
|
```vue
|
|
<template>
|
|
<Form ref="formRef" :fields="fields" />
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref } from 'vue'
|
|
|
|
const formRef = ref(null)
|
|
|
|
// Access exposed methods
|
|
const validateForm = () => formRef.value.validateForm()
|
|
const resetForm = () => formRef.value.resetForm()
|
|
</script>
|
|
```
|
|
|
|
### `validateForm()`
|
|
- **Description:** Validates the entire form and returns validation status
|
|
- **Returns:** `Boolean` - `true` if valid, `false` if invalid
|
|
- **Side Effect:** Updates form error state
|
|
|
|
### `getCurrentFormData()`
|
|
- **Description:** Gets the current form data object
|
|
- **Returns:** `Object` - Current form data
|
|
|
|
### `resetForm()`
|
|
- **Description:** Resets form to initial state and clears all errors
|
|
- **Returns:** `void`
|
|
|
|
### `setFieldError(fieldName, error)`
|
|
- **Description:** Sets an error message for a specific field
|
|
- **Parameters:**
|
|
- `fieldName` (String) - The field name
|
|
- `error` (String) - Error message to display
|
|
|
|
### `clearFieldError(fieldName)`
|
|
- **Description:** Clears the error for a specific field
|
|
- **Parameters:**
|
|
- `fieldName` (String) - The field name to clear
|
|
|
|
## Usage Examples
|
|
|
|
### Basic Contact Form
|
|
```vue
|
|
<script setup>
|
|
import { ref } from 'vue'
|
|
|
|
const formData = ref({})
|
|
|
|
const contactFields = [
|
|
{
|
|
name: 'firstName',
|
|
label: 'First Name',
|
|
type: 'text',
|
|
required: true,
|
|
cols: 12,
|
|
md: 6
|
|
},
|
|
{
|
|
name: 'lastName',
|
|
label: 'Last Name',
|
|
type: 'text',
|
|
required: true,
|
|
cols: 12,
|
|
md: 6
|
|
},
|
|
{
|
|
name: 'email',
|
|
label: 'Email',
|
|
type: 'text',
|
|
format: 'email',
|
|
required: true,
|
|
cols: 12
|
|
},
|
|
{
|
|
name: 'message',
|
|
label: 'Message',
|
|
type: 'textarea',
|
|
rows: 5,
|
|
required: true,
|
|
cols: 12
|
|
}
|
|
]
|
|
|
|
const handleSubmit = async (data) => {
|
|
try {
|
|
await sendContactForm(data)
|
|
alert('Form submitted successfully!')
|
|
} catch (error) {
|
|
console.error('Submission failed:', error)
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<Form
|
|
:fields="contactFields"
|
|
v-model:form-data="formData"
|
|
@submit="handleSubmit"
|
|
submit-button-text="Send Message"
|
|
/>
|
|
</template>
|
|
```
|
|
|
|
### User Registration Form
|
|
```vue
|
|
<script setup>
|
|
import { ref } from 'vue'
|
|
|
|
const registrationData = ref({})
|
|
|
|
const registrationFields = [
|
|
{
|
|
name: 'username',
|
|
label: 'Username',
|
|
type: 'text',
|
|
required: true,
|
|
validate: (value) => {
|
|
if (value && value.length < 3) {
|
|
return 'Username must be at least 3 characters'
|
|
}
|
|
return null
|
|
}
|
|
},
|
|
{
|
|
name: 'email',
|
|
label: 'Email',
|
|
type: 'text',
|
|
format: 'email',
|
|
required: true
|
|
},
|
|
{
|
|
name: 'age',
|
|
label: 'Age',
|
|
type: 'number',
|
|
min: 18,
|
|
max: 100,
|
|
required: true
|
|
},
|
|
{
|
|
name: 'country',
|
|
label: 'Country',
|
|
type: 'select',
|
|
required: true,
|
|
options: [
|
|
{ label: 'United States', value: 'us' },
|
|
{ label: 'Canada', value: 'ca' },
|
|
{ label: 'United Kingdom', value: 'uk' }
|
|
]
|
|
},
|
|
{
|
|
name: 'terms',
|
|
label: 'I agree to the terms and conditions',
|
|
type: 'checkbox',
|
|
required: true,
|
|
validate: (value) => {
|
|
if (!value) {
|
|
return 'You must agree to the terms and conditions'
|
|
}
|
|
return null
|
|
}
|
|
}
|
|
]
|
|
</script>
|
|
|
|
<template>
|
|
<Form
|
|
:fields="registrationFields"
|
|
v-model:form-data="registrationData"
|
|
@submit="handleRegistration"
|
|
submit-button-text="Register"
|
|
show-cancel-button
|
|
@cancel="handleCancel"
|
|
/>
|
|
</template>
|
|
```
|
|
|
|
### Survey Form with Custom Validation
|
|
```vue
|
|
<script setup>
|
|
import { ref } from 'vue'
|
|
|
|
const surveyData = ref({})
|
|
|
|
const surveyFields = [
|
|
{
|
|
name: 'satisfaction',
|
|
label: 'How satisfied are you with our service?',
|
|
type: 'radio',
|
|
required: true,
|
|
options: [
|
|
{ label: 'Very Satisfied', value: '5' },
|
|
{ label: 'Satisfied', value: '4' },
|
|
{ label: 'Neutral', value: '3' },
|
|
{ label: 'Dissatisfied', value: '2' },
|
|
{ label: 'Very Dissatisfied', value: '1' }
|
|
],
|
|
cols: 12
|
|
},
|
|
{
|
|
name: 'feedback',
|
|
label: 'Additional Feedback',
|
|
type: 'textarea',
|
|
rows: 4,
|
|
placeholder: 'Please share your thoughts...',
|
|
cols: 12,
|
|
onChangeOverride: (value, fieldName, formData) => {
|
|
// Custom logic for this field only
|
|
console.log(`Feedback length: ${value?.length || 0} characters`)
|
|
}
|
|
},
|
|
{
|
|
name: 'recommend',
|
|
label: 'Would you recommend us to others?',
|
|
type: 'checkbox',
|
|
cols: 12
|
|
}
|
|
]
|
|
|
|
const handleSurveySubmit = (data) => {
|
|
console.log('Survey submitted:', data)
|
|
}
|
|
|
|
const handleFieldChange = (event) => {
|
|
console.log('Global change handler:', event)
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<Form
|
|
:fields="surveyFields"
|
|
v-model:form-data="surveyData"
|
|
@submit="handleSurveySubmit"
|
|
@change="handleFieldChange"
|
|
submit-button-text="Submit Survey"
|
|
/>
|
|
</template>
|
|
```
|
|
|
|
### File Upload Form
|
|
```vue
|
|
<script setup>
|
|
import { ref } from 'vue'
|
|
|
|
const uploadData = ref({})
|
|
|
|
const uploadFields = [
|
|
{
|
|
name: 'title',
|
|
label: 'Document Title',
|
|
type: 'text',
|
|
required: true,
|
|
cols: 12
|
|
},
|
|
{
|
|
name: 'category',
|
|
label: 'Category',
|
|
type: 'select',
|
|
required: true,
|
|
options: [
|
|
{ label: 'Reports', value: 'reports' },
|
|
{ label: 'Presentations', value: 'presentations' },
|
|
{ label: 'Documents', value: 'documents' }
|
|
],
|
|
cols: 12,
|
|
md: 6
|
|
},
|
|
{
|
|
name: 'uploadDate',
|
|
label: 'Upload Date',
|
|
type: 'date',
|
|
required: true,
|
|
cols: 12,
|
|
md: 6
|
|
},
|
|
{
|
|
name: 'files',
|
|
label: 'Select Files',
|
|
type: 'file',
|
|
accept: '.pdf,.doc,.docx,.ppt,.pptx',
|
|
multiple: true,
|
|
required: true,
|
|
cols: 12
|
|
},
|
|
{
|
|
name: 'description',
|
|
label: 'Description',
|
|
type: 'textarea',
|
|
rows: 3,
|
|
helpText: 'Optional description of the uploaded files',
|
|
cols: 12
|
|
}
|
|
]
|
|
</script>
|
|
|
|
<template>
|
|
<Form
|
|
:fields="uploadFields"
|
|
v-model:form-data="uploadData"
|
|
@submit="handleFileUpload"
|
|
submit-button-text="Upload Files"
|
|
/>
|
|
</template>
|
|
```
|
|
|
|
## Form State Management
|
|
|
|
### Controlled Mode (External Form Data)
|
|
When you provide a `formData` prop, the component operates in controlled mode:
|
|
|
|
```vue
|
|
<script setup>
|
|
import { ref } from 'vue'
|
|
|
|
// External form state
|
|
const formData = ref({
|
|
name: 'John Doe',
|
|
email: 'john@example.com'
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<Form
|
|
:fields="fields"
|
|
v-model:form-data="formData"
|
|
@submit="handleSubmit"
|
|
/>
|
|
</template>
|
|
```
|
|
|
|
### Uncontrolled Mode (Internal Form Data)
|
|
When no `formData` is provided, the component manages its own internal state:
|
|
|
|
```vue
|
|
<template>
|
|
<Form
|
|
:fields="fields"
|
|
@submit="handleSubmit"
|
|
/>
|
|
</template>
|
|
|
|
<script setup>
|
|
const handleSubmit = (data) => {
|
|
// Data is passed to the submit handler
|
|
console.log('Form data:', data)
|
|
}
|
|
</script>
|
|
```
|
|
|
|
## Validation
|
|
|
|
### Built-in Validation
|
|
- **Required fields** - Validates that required fields are not empty
|
|
- **Email format** - Validates email format when `format: 'email'` is used
|
|
- **Number ranges** - Validates min/max values for number fields
|
|
|
|
### Custom Validation
|
|
Each field can have a custom validation function:
|
|
|
|
```javascript
|
|
{
|
|
name: 'password',
|
|
label: 'Password',
|
|
type: 'text',
|
|
required: true,
|
|
validate: (value) => {
|
|
if (value && value.length < 8) {
|
|
return 'Password must be at least 8 characters long'
|
|
}
|
|
if (value && !/(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/.test(value)) {
|
|
return 'Password must contain at least one lowercase letter, one uppercase letter, and one number'
|
|
}
|
|
return null // Valid
|
|
}
|
|
}
|
|
```
|
|
|
|
### Validation Timing
|
|
- **On change** - When `validateOnChange` is `true` (default)
|
|
- **On submit** - Always validates on form submission
|
|
- **Manual** - Using the exposed `validateForm()` method
|
|
|
|
## Responsive Layout
|
|
|
|
The component uses Vuetify's grid system for responsive layouts:
|
|
|
|
```javascript
|
|
{
|
|
name: 'field',
|
|
label: 'Field',
|
|
type: 'text',
|
|
cols: 12, // Full width on extra small screens
|
|
sm: 12, // Full width on small screens
|
|
md: 6, // Half width on medium screens
|
|
lg: 4 // One-third width on large screens
|
|
}
|
|
```
|
|
|
|
## Styling
|
|
|
|
The component uses Vuetify's design system with:
|
|
- **Outlined variants** for consistent appearance
|
|
- **Comfortable density** for optimal spacing
|
|
- **Error state styling** for validation feedback
|
|
- **Required field indicators** with red asterisks
|
|
- **Responsive design** that adapts to screen size
|
|
|
|
## Best Practices
|
|
|
|
1. **Use meaningful field names** that reflect the data structure
|
|
2. **Provide clear labels and help text** for better user experience
|
|
3. **Implement proper validation** for data integrity
|
|
4. **Consider responsive layout** for different screen sizes
|
|
5. **Handle form submission errors** gracefully
|
|
6. **Use controlled mode** when form data needs to be managed externally
|
|
7. **Leverage custom validation** for business-specific rules
|
|
8. **Test with various field combinations** to ensure proper behavior
|
|
9. **Use appropriate field types** for better user experience
|
|
10. **Provide meaningful default values** when appropriate
|
|
|
|
## Accessibility
|
|
|
|
The component includes:
|
|
- **Proper form semantics** with native HTML form elements
|
|
- **Label associations** for screen readers
|
|
- **Error message announcements** for validation feedback
|
|
- **Keyboard navigation** support throughout the form
|
|
- **Focus management** for better usability
|
|
- **Required field indicators** for clarity
|
|
|
|
## Browser Support
|
|
|
|
Compatible with all modern browsers that support:
|
|
- Vue 3 Composition API
|
|
- Vuetify 3 components
|
|
- ES6+ features
|
|
- CSS Grid and Flexbox
|
|
|
|
## Dependencies
|
|
|
|
- **Vue 3** with Composition API
|
|
- **Vuetify 3** components (v-form, v-text-field, v-textarea, v-select, v-checkbox, v-radio-group, v-file-input, v-btn)
|
|
- **Modern JavaScript** features (ES6+) |