Form Handling
Handle form submissions with diff:submit directive.
Basic Form Submission
blade
<form diff:submit="submit">
<input type="text" diff:model="name">
<button type="submit">Submit</button>
</form>Component:
php
use Diffyne\Attributes\Invokable;
class ContactForm extends Component
{
public string $name = '';
#[Invokable]
public function submit()
{
// Handle form submission
logger('Form submitted:', ['name' => $this->name]);
}
}Note: Form default submission is automatically prevented when using diff:submit.
Form with Validation
blade
<form diff:submit="submit">
<div>
<label>Email</label>
<input
type="email"
diff:model="email"
class="border rounded px-3 py-2">
<span diff:error="email" class="text-red-500"></span>
</div>
<div>
<label>Password</label>
<input
type="password"
diff:model="password"
class="border rounded px-3 py-2">
<span diff:error="password" class="text-red-500"></span>
</div>
<button
type="submit"
diff:loading.class.opacity-50
class="bg-blue-500 text-white px-4 py-2 rounded">
Login
<span diff:loading>...</span>
</button>
</form>
@if(session('success'))
<div class="text-green-500">{{ session('success') }}</div>
@endifComponent:
php
use Diffyne\Attributes\Invokable;
class LoginForm extends Component
{
public string $email = '';
public string $password = '';
protected function rules(): array
{
return [
'email' => 'required|email',
'password' => 'required|min:8',
];
}
protected function messages(): array
{
return [
'email.required' => 'Please enter your email address',
'email.email' => 'Please enter a valid email',
'password.min' => 'Password must be at least 8 characters',
];
}
#[Invokable]
public function submit()
{
$validated = $this->validate();
if (auth()->attempt($validated)) {
session()->flash('success', 'Logged in successfully!');
$this->redirect('/dashboard');
}
$this->addError('email', 'Invalid credentials');
}
}Model Binding in Forms
By default, diff:model updates local state and syncs with server on change events. For forms, this is typically what you want:
blade
<form diff:submit="createUser">
<input diff:model="name">
<input diff:model="email">
<input diff:model="phone">
<button type="submit">Create User</button>
</form>Note: Inputs update local state as you type, and send server requests on change/blur events.
Multi-field Validation
blade
<form diff:submit="register">
<div>
<input diff:model="username" placeholder="Username">
<span diff:error="username"></span>
</div>
<div>
<input type="email" diff:model="email" placeholder="Email">
<span diff:error="email"></span>
</div>
<div>
<input type="password" diff:model="password" placeholder="Password">
<span diff:error="password"></span>
</div>
<div>
<input type="password" diff:model="passwordConfirmation" placeholder="Confirm Password">
<span diff:error="passwordConfirmation"></span>
</div>
<button type="submit">Register</button>
</form>Component:
php
use App\Models\User;
class RegisterForm extends Component
{
public string $username = '';
public string $email = '';
public string $password = '';
public string $passwordConfirmation = '';
protected function rules(): array
{
return [
'username' => 'required|min:3|unique:users',
'email' => 'required|email|unique:users',
'password' => 'required|min:8|confirmed',
];
}
public function register()
{
$validated = $this->validate();
$user = User::create([
'username' => $validated['username'],
'email' => $validated['email'],
'password' => bcrypt($validated['password']),
]);
auth()->login($user);
$this->redirect('/dashboard');
}
}Form Reset
Reset form after successful submission:
blade
<form diff:submit="submit">
<input diff:model="message">
<button type="submit">Send</button>
</form>
@if($sent)
<div class="text-green-500">Message sent!</div>
@endifComponent:
php
use Diffyne\Attributes\Invokable;
class MessageForm extends Component
{
public string $message = '';
public bool $sent = false;
#[Invokable]
public function submit()
{
$this->validate(['message' => 'required|min:10']);
// Send message...
$this->sent = true;
$this->reset('message'); // Clear the input
// Or reset all properties
// $this->reset();
}
}Real-time Field Validation
Validate individual fields as user types:
blade
<form diff:submit="submit">
<div>
<input
type="email"
diff:model.lazy="email"
diff:change="validateEmail">
<span diff:error="email"></span>
</div>
<button type="submit">Submit</button>
</form>Component:
php
use Diffyne\Attributes\Invokable;
class SignupForm extends Component
{
public string $email = '';
#[Invokable]
public function validateEmail()
{
$this->validateOnly('email');
}
protected function rules(): array
{
return [
'email' => 'required|email|unique:users',
];
}
#[Invokable]
public function submit()
{
$this->validate();
// Process signup...
}
}Complex Forms
Dynamic Fields
blade
<form diff:submit="submit">
@foreach($emails as $index => $email)
<div>
<input
diff:model="emails.{{ $index }}"
placeholder="Email {{ $index + 1 }}">
<button
type="button"
diff:click="removeEmail({{ $index }})">
Remove
</button>
</div>
@endforeach
<button type="button" diff:click="addEmail">Add Email</button>
<button type="submit">Submit</button>
</form>Component:
php
use Diffyne\Attributes\Invokable;
class MultiEmailForm extends Component
{
public array $emails = [''];
#[Invokable]
public function addEmail()
{
$this->emails[] = '';
}
#[Invokable]
public function removeEmail($index)
{
unset($this->emails[$index]);
$this->emails = array_values($this->emails);
}
#[Invokable]
public function submit()
{
$this->validate([
'emails.*' => 'required|email',
]);
// Process emails...
}
}File Upload
Diffyne provides a built-in file upload system with temporary storage. See the File Uploads documentation for complete details.
Quick Example:
blade
<input type="file" diff:model="image" accept="image/*">
@if($image)
<img src="{{ $component->getTemporaryFilePreviewUrl($image) }}" alt="Preview">
@endif
<button diff:click="saveImage">Save</button>php
use Diffyne\Attributes\Invokable;
class ImageUpload extends Component
{
public ?string $image = null;
#[Invokable]
public function saveImage(): void
{
if ($this->image) {
$path = $this->moveTemporaryFile(
$this->image,
'uploads/' . uniqid() . '.jpg',
'public'
);
// File is now in permanent storage
}
}
}Conditional Fields
blade
<form diff:submit="submit">
<select diff:model.live="type">
<option value="individual">Individual</option>
<option value="business">Business</option>
</select>
@if($type === 'individual')
<input diff:model="firstName" placeholder="First Name">
<input diff:model="lastName" placeholder="Last Name">
@else
<input diff:model="companyName" placeholder="Company Name">
<input diff:model="taxId" placeholder="Tax ID">
@endif
<button type="submit">Submit</button>
</form>Component:
php
class DynamicForm extends Component
{
public string $type = 'individual';
public string $firstName = '';
public string $lastName = '';
public string $companyName = '';
public string $taxId = '';
protected function rules(): array
{
if ($this->type === 'individual') {
return [
'firstName' => 'required|min:2',
'lastName' => 'required|min:2',
];
}
return [
'companyName' => 'required|min:3',
'taxId' => 'required',
];
}
#[Invokable]
public function submit()
{
$this->validate();
// Process form...
}
}Loading States
Show feedback during form submission:
blade
<form diff:submit="submit">
<input diff:model="email">
<button
type="submit"
diff:loading.class.opacity-50.cursor-not-allowed>
<span diff:loading>Submitting...</span>
Submit
</button>
</form>
<div diff:loading class="text-blue-500">
Processing your request...
</div>Best Practices
1. Form Submission is Prevented Automatically
blade
{{-- Good - default behavior prevents reload --}}
<form diff:submit="submit">2. Choose Model Binding Strategy
blade
{{-- Default: Updates local state, syncs on change --}}
<form diff:submit="submit">
<input diff:model="field1">
<input diff:model="field2">
</form>
{{-- Live updates: Immediate server sync --}}
<input diff:model.live="search">3. Validate on Server
php
use Diffyne\Attributes\Invokable;
#[Invokable]
public function submit()
{
// Always validate server-side
$this->validate();
// Process form...
}4. Provide User Feedback
blade
<button type="submit" diff:loading.class.opacity-50>
<span diff:loading>Submitting...</span>
Submit
</button>
@if($success)
<div class="text-green-500">Form submitted successfully!</div>
@endif5. Handle Errors Gracefully
php
use Diffyne\Attributes\Invokable;
#[Invokable]
public function submit()
{
try {
$this->validate();
// Process form...
$this->success = true;
} catch (\Exception $e) {
$this->addError('general', 'An error occurred. Please try again.');
}
}Next Steps
Master form handling:
- Validation - Validate user input in detail
- Data Binding - Two-way data synchronization
- Loading States - Improve user experience
- Contact Form Example - Complete working example
