Table of Contents
You’ve built a beautiful form. User selects “Project Manager” from a dropdown. Form submits. Backend throws: role_id is required.
What happened? The dropdown had only one option. Vue’s v-model never fired.
The Problem
Vue’s v-model on <select> elements only updates when a @change event fires.
Single-option dropdowns never fire @change because the user can’t change anything.
What the User Sees
<select v-model="form.role_id">
<option value="3">Project Manager</option>
</select>
UI shows: “Project Manager” (looks selected).
Reality: form.role_id = null.
Why It Breaks
- Component initializes with
form.role_id = null - Dropdown renders with one option
- Browser displays that option as “selected” (visual default)
- No user interaction = no @change event
v-modelnever updates- Form submits
null
The Solutions
Option 1: Auto-Select in Lifecycle Hook (Recommended)
mounted() {
// Auto-select if only one option
if (this.roleOptions.length === 1) {
this.form.role_id = this.roleOptions[0].id;
}
}
Pros: Clean, explicit, works with any UI library.
Cons: Needs lifecycle hook in every component with single-option dropdowns.
Option 2: Backend Defensive Handling
public function assignRole(Request $request)
{
// Fallback to default if frontend didn't send value
$roleId = $request->input('role_id')
?? Role::getDefaultForContext($request->input('department_id'));
// Validate the final value
$request->merge(['role_id' => $roleId]);
$validated = $request->validate([
'role_id' => 'required|exists:roles,id',
]);
// ... proceed with assignment
}
Pros: Fails gracefully, works even if frontend changes.
Cons: Backend needs context to know which default to use.
Option 3: Disable Dropdown When Single Option
<select
v-model="form.role_id"
:disabled="roleOptions.length === 1"
>
<option
v-for="role in roleOptions"
:key="role.id"
:value="role.id"
>
{{ role.name }}
</option>
</select>
Pros: Visual clarity that choice is locked.
Cons: Still need lifecycle hook or backend fallback.
Recommended Approach: Defense in Depth
Combine Option 1 + Option 2:
- Frontend: Auto-select in
mounted() - Backend: Defensive null handling with context-aware defaults
This way, if the frontend breaks (JS error, race condition, browser quirk), the backend still handles it gracefully.
The Lesson
Never trust frontend validation alone. Single-option dropdowns are a UI/UX trap—they look selected but aren’t.
Always validate and default on the backend. Your future self (and your error logs) will thank you.
Leave a Reply