Mostrar campos de formulario en Laravel Livewire condicionalmente

Video thumbnail

Recientemente me preguntaron cómo podríamos mostrar un campo de forma condicional. Por ejemplo, si aquí selecciono cualquier valor —supongamos que selecciono "persona"—, me gustaría que aparezca un campo adicional. Así que, para esto, hay varias consideraciones que debemos tener en cuenta.

Voy a partir del ejemplo paso por paso que hicimos en el curso de Livewire, aunque perfectamente lo puedes adaptar a un formulario normal sin pasos. La lógica es la misma, ya que este sistema paso a paso que implementamos es bastante modular —como siempre he comentado.

Consideraciones iniciales

Lo primero es que, si vas a crear la migración (o supongo que como es condicional...), entiéndase que si aquí seleccionamos "compañía" —porque "persona" es el valor por defecto— entonces quiero mostrar ese campo adicional. Si es "persona", no mostraré nada.

Esto es simplemente una condición que se puede evaluar mediante un condicional, así que puede ser cualquier cosa.

Obviamente, ese campo adicional debe existir en la entidad, por lo que tendrías que crearlo. En mi caso no lo voy a crear porque esto es solo una demostración, así que no me importa si se guarda o no en la base de datos. Pero lo lógico es que lo guardes, porque si no, no estás haciendo nada con ese campo.

Ese campo extra debería estar definido también en el modelo, y se puede marcar como nullable, o puedes definir un valor por defecto si no está establecido. Eso ya depende de ti y de cómo quieras manejarlo. Pero, en resumen, no tiene mucho misterio.

Validaciones condicionales en formularios

Aquí lo más recomendable es que utilices clases de tipo formulario con el método rules(), ya que ahí puedes definir las reglas de validación condicionales. Por ejemplo, si cierto campo tiene determinado valor, puedes aplicar o no aplicar ciertas validaciones.

En este caso, yo no lo hice así. Si revisamos la clase, las validaciones están definidas directamente, por lo que no puedo hacer mucho en este ejemplo. Pero repito: usar clases de formulario con rules() es lo mejor, ya que puedes evaluar condiciones y aplicar o no validaciones según lo necesites.

También podrías aplicar validaciones directamente usando el método validator() —si mal no recuerdo, hay como tres formas—. Pero, como ya tengo esta estructura, no quiero cambiar mucho para respetar lo que hicimos en el curso. Esto es solo una demostración.

Implementación básica en el componente

En este caso, estoy utilizando Volt, ya que es el que me está funcionando bien. El otro componente parece tener problemas con los eventos.

Lo primero es definir el nuevo atributo del campo. Voy a llamarlo extra. Aquí deberías tener también sus validaciones. Como es una demo, solo lo defino por simplicidad.

Luego, lo pintamos en el HTML. Lo coloco justo después del campo de tipo, y puede ser cualquier tipo de campo, pero usaré otro select. Lo llamo 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>

Ocultar el campo condicionalmente

Por defecto, no quiero que aparezca el campo. Aquí colocamos la condición para mostrarlo: si el tipo seleccionado es igual a "company", entonces mostramos el campo.

@if ($type === 'company')
   <!-- Mostrar el campo extra -->
@endif

Esto lo puedes imprimir para verificar. Por ejemplo, seleccionas "persona", no se muestra nada. Seleccionas "compañía", aparece el campo extra. Esto se logra agregando .live, que hay que usar con cuidado.

Limpieza del valor cuando cambia la opción

El problema es que si seleccionamos "persona" después de haber seleccionado "compañía", el valor del campo extra queda definido. Para limpiar esto, se puede usar un método del ciclo de vida de Livewire, como updated.

Este método recibe la propiedad que se ha actualizado. Por ejemplo:

    public function updated($property)
    {
        // $property: The name of the current property that was updated

        if ($property === 'type' && $this->type != 'company') {
            $this->extra = '';
        }
    }

Esto limpia el campo extra automáticamente cuando se cambia el tipo a algo diferente de "compañía".

Validaciones locales (opcional)

Todavía no tenemos validación en esta demo. Como la estructura actual no permite validación condicional fácilmente, podrías aplicar una validación local manual: cuando hagas submit, verificas si el campo extra está definido, y lo validas en ese momento.

Acepto recibir anuncios de interes sobre este Blog.

Vemos un mecanismo que nos permite crear campos de formulario opcionales mediante condicionales.

- Andrés Cruz

In english