Form System
The library includes a type-safe form system with validation, auto-generated forms, and composables.
useForm Composable
The useForm composable provides type-safe form state management and submission.
Basic Usage
<script setup lang="ts">
import { useForm, DButton, DFormInput } from '@omnitend/dashboard-for-laravel'
const form = useForm({
name: '',
email: '',
password: ''
})
const handleSubmit = async () => {
await form.post('/api/users', {
onSuccess: (data) => {
console.log('User created:', data)
},
onError: (errors) => {
console.log('Validation failed:', errors)
}
})
}
</script>
<template>
<form @submit.prevent="handleSubmit">
<DFormGroup label="Name" :error="form.errors.name">
<DFormInput v-model="form.data.name" />
</DFormGroup>
<DFormGroup label="Email" :error="form.errors.email">
<DFormInput v-model="form.data.email" type="email" />
</DFormGroup>
<DButton type="submit" :disabled="form.processing">
{{ form.processing ? 'Submitting...' : 'Submit' }}
</DButton>
</form>
</template>
Form State
The useForm composable returns a form object with the following properties:
interface UseFormReturn<T> {
// Form data (reactive) - access via form.data.fieldName
data: T
// State
processing: boolean // Is form submitting?
errors: ValidationErrors // Validation errors
hasErrors: ComputedRef<boolean> // Are there any errors?
recentlySuccessful: boolean // Was last submit successful?
// Methods
post(url: string, options?: FormSubmitOptions): Promise<any>
put(url: string, options?: FormSubmitOptions): Promise<any>
patch(url: string, options?: FormSubmitOptions): Promise<any>
delete(url: string, options?: FormSubmitOptions): Promise<any>
reset(): void // Reset to initial values
clearErrors(): void // Clear all errors
clearError(field: string): void // Clear specific field error
}
HTTP Methods
// POST request
await form.post('/api/users')
// PUT request
await form.put('/api/users/1')
// PATCH request
await form.patch('/api/users/1')
// DELETE request
await form.delete('/api/users/1')
Callbacks
await form.post('/api/users', {
onSuccess: (data) => {
console.log('Success:', data)
// Navigate away, show toast, etc.
},
onError: (errors) => {
console.log('Validation failed:', errors)
}
})
Error Handling
<script setup lang="ts">
const form = useForm({
email: ''
})
</script>
<template>
<!-- Show error for specific field -->
<DFormGroup label="Email" :error="form.errors.email">
<DFormInput v-model="form.data.email" />
</DFormGroup>
<!-- Clear specific error -->
<DButton @click="form.clearError('email')">
Clear Email Error
</DButton>
<!-- Clear all errors -->
<DButton @click="form.clearErrors()">
Clear All Errors
</DButton>
</template>
Tips
- Bind to
form.data.*(orform.field('name')) so validation state stays in sync. - Use
options.transformto reshape payloads before sending. reset()restores the initial snapshot; pass a list of keys to reset specific fields only.
DXBasicForm Component
Auto-generate forms from field definitions:
<script setup lang="ts">
import { useForm, DXBasicForm } from '@omnitend/dashboard-for-laravel'
import type { FieldDefinition } from '@omnitend/dashboard-for-laravel'
const form = useForm({
name: '',
email: '',
country: 'UK',
accept_terms: false
})
const fields: FieldDefinition[] = [
{
key: 'name',
label: 'Full Name',
type: 'text',
required: true,
placeholder: 'Enter your name'
},
{
key: 'email',
label: 'Email Address',
type: 'email',
required: true
},
{
key: 'country',
label: 'Country',
type: 'select',
required: true,
options: [
{ value: 'UK', text: 'United Kingdom' },
{ value: 'US', text: 'United States' },
{ value: 'CA', text: 'Canada' }
]
},
{
key: 'accept_terms',
label: 'I accept the terms and conditions',
type: 'checkbox',
required: true
}
]
const handleSubmit = async () => {
await form.post('/api/users')
}
</script>
<template>
<DXBasicForm
:fields="fields"
:form="form"
submit-text="Create Account"
@submit="handleSubmit"
/>
</template>
Field Types
Supported field types:
text- Text inputemail- Email inputpassword- Password inputtel- Telephone inputnumber- Number inputurl- URL inputdate- Date pickerdatetime-local- Date and time pickertime- Time pickertextarea- Multi-line textselect- Dropdown select (requiresoptions)checkbox- Checkboxradio- Radio button group (requiresoptions)
Field Definition
interface FieldDefinition {
key: string // Form field key (must match form data key)
type: FieldType // Input type
label?: string // Field label
placeholder?: string // Placeholder text
required?: boolean // Is field required?
options?: Array<{ value: any; text: string; disabled?: boolean }> // For select/radio fields
rows?: number // Number of rows for textarea (default: 3)
help?: string // Help text displayed below field
class?: string // CSS class for the form group
inputProps?: Record<string, any> // Additional props to pass to input
}
DXForm Component
Advanced form component with more control:
<script setup lang="ts">
import { useForm, DXForm, DFormInput, DButton } from '@omnitend/dashboard-for-laravel'
const form = useForm({
name: '',
email: ''
})
const handleSubmit = async () => {
await form.post('/api/users')
}
</script>
<template>
<DXForm :form="form" @submit="handleSubmit">
<DFormGroup label="Name">
<DFormInput v-model="form.data.name" />
</DFormGroup>
<DFormGroup label="Email">
<DFormInput v-model="form.data.email" type="email" />
</DFormGroup>
<DButton type="submit">
Submit
</DButton>
</DXForm>
</template>
Laravel Integration
Form Requests
On the Laravel side, use BaseFormRequest for validation:
use OmniTend\LaravelDashboard\Http\Requests\BaseFormRequest;
class CreateUserRequest extends BaseFormRequest
{
public function rules()
{
return [
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'email', 'unique:users'],
'password' => ['required', 'string', 'min:8'],
];
}
}
API Responses
Use the HasApiResponses trait for consistent responses:
use OmniTend\LaravelDashboard\Traits\HasApiResponses;
class UserController extends Controller
{
use HasApiResponses;
public function store(CreateUserRequest $request)
{
$user = User::create($request->validated());
return $this->success($user, 'User created successfully');
}
}
The form composable will automatically:
- Parse validation errors
- Update
form.errors - Call the appropriate callback
Example: Complete Form
<script setup lang="ts">
import { useForm, DCard, DXBasicForm } from '@omnitend/dashboard-for-laravel'
import { useRouter } from 'vue-router'
const router = useRouter()
const form = useForm({
name: '',
email: '',
password: ''
})
const fields = [
{
key: 'name',
label: 'Name',
type: 'text',
required: true
},
{
key: 'email',
label: 'Email',
type: 'email',
required: true
},
{
key: 'password',
label: 'Password',
type: 'password',
required: true
}
]
const handleSubmit = async () => {
await form.post('/api/users', {
onSuccess: () => {
router.push('/users')
},
onError: (errors) => {
console.log('Validation errors:', errors)
}
})
}
</script>
<template>
<DCard>
<template #header>
<h3>Create User</h3>
</template>
<DXBasicForm
:fields="fields"
:form="form"
submit-text="Create User"
@submit="handleSubmit"
/>
</DCard>
</template>
Next Steps
- Component Reference - DXForm component API
- Component Reference - DXBasicForm component API
- Examples - More form examples