Show form fields in Laravel Livewire conditionally
I was recently asked how we could conditionally display a field. For example, if I select any value here—let's say I select "person"—I'd like an additional field to appear. So, for this, there are several considerations we need to take into account.
I'll start with the step-by-step example we did in the Livewire course, although you can perfectly adapt it to a normal form without steps. The logic is the same, since this step-by-step system we implemented is quite modular—as I've always said.
Initial considerations
The first thing is that, if you're going to create the migration (or I suppose since it's conditional...), understand that if we select "company" here—because "person" is the default value—then I want to show that additional field. If it's "person," I won't show anything.
This is simply a condition that can be evaluated using a conditional, so it can be anything.
Obviously, that additional field must exist in the entity, so you'd have to create it. In my case, I'm not going to create it because this is just a demonstration, so I don't care whether it's saved in the database or not. But it makes sense to save it, because if not, you're not doing anything with that field.
That additional field should also be defined in the model, and you can mark it as nullable, or you can define a default value if it isn't set. That's up to you and how you want to handle it. But, in short, there's not much mystery to it.
Conditional validations in forms
Here, it's best to use form-type classes with the rules() method, since you can define conditional validation rules there. For example, if a certain field has a certain value, you can apply or deny certain validations.
In this case, I didn't do it that way. If we look at the class, the validations are defined directly, so I can't do much in this example. But again: using form classes with rules() is the best, since you can evaluate conditions and apply or deny validations as needed.
You could also apply validations directly using the validator() method—if I remember correctly, there are about three ways. But since I already have this structure, I don't want to change much to respect what we did in the course. This is just a demonstration.
Basic implementation in the component
In this case, I'm using Volt, as it's working well for me. The other component seems to be having issues with events.
The first thing is to define the new field attribute. I'm going to call it extra. You should also have its validations here. Since this is a demo, I'm just defining it for simplicity.
Then, we add it to the HTML. I place it right after the field type, and it can be any field type, but I'll use another select. I'll call it extra.
new #[Layout('layouts.contact')] class extends Component {
***
#[Validate('required')]
public $type;
// demo
public $extra;
***
function mount(?int $id = null, ?int $step = null, string $subject = '')
{
***
}
public function updated($property)
{
// $property: The name of the current property that was updated
if ($property === 'type' && $this->type != 'company') {
$this->extra = '';
}
}
function submit()
{
***
}
}; ?>
<div>
***
@if ($step == 1)
<form wire:submit.prevent='submit' class="flex flex-col max-w-sm mx-auto">
***
<flux:label>{{ __('Type') }}</flux:label>
<flux:error name='type' />
<flux:select wire:model.live='type'>
<option value=""></option>
<option value="person">{{ __('Person') }}</option>
<option value="company">{{ __('Company') }}</option>
</flux:select>
@if ($type == 'company')
<flux:select wire:model='extra' :label='__("Extra")'>
<option value=""></option>
<option value="extra1">{{ __('Extra 1') }}</option>
<option value="extra2">{{ __('Extra 2') }}</option>
</flux:select>
@endif
<flux:label>{{ __('Message') }}</flux:label>
***
<div class="flex mt-5 gap-3">
<flux:button variant='primary' type='submit'>{{ __('Send') }}</flux:button>
</div>
</form>
</div>
</div>
Conditionally hide field
By default, I don't want the field to appear. Here we set the condition for displaying it: if the selected type is equal to "company," then we display the field.
@if ($type === 'company')
<!-- Mostrar el campo extra -->
@endif
You can print this to verify. For example, if you select "person," nothing is displayed. If you select "company," the extra field appears. This is achieved by adding .live, which must be used carefully.
Cleaning up the value when the option changes
The problem is that if we select "person" after selecting "company," the value of the extra field remains defined. To clean this up, you can use a Livewire lifecycle method, such as updated.
This method receives the updated property. For example:
public function updated($property)
{
// $property: The name of the current property that was updated
if ($property === 'type' && $this->type != 'company') {
$this->extra = '';
}
}
This automatically clears the extra field when the type is changed to something other than "company".
Local Validations (Optional)
We don't have validation in this demo yet. Since the current structure doesn't easily allow for conditional validation, you could apply manual local validation: when you submit, check if the extra field is defined, and validate it at that time.
I agree to receive announcements of interest about this Blog.
We see a mechanism that allows us to create optional form fields using conditionals.
- Andrés Cruz