Ahsan
Ahsan

Reputation: 1359

Laravel Livewire: load livewire component with button click

There are 3 livewire components UserIsExpired, UserIsActive and UserIsPending and 3 buttons respective to each component. When a button is clicked, it should replace previous component with its respective component.

<button wire:click="$emit(active)">{{ __('Active') }}</button>
<button wire:click="$emit(pending)">{{ __('Pending') }}</button>
<button wire:click="$emit(expired)">{{ __('Expired') }}</button>

In view

<livewire:user-is-active :active="$active"/>
<livewire:user-is-pending :pending="$pending"/>
<livewire:user-is-expired :expired="$expired"/>

Component example

class UserIsExpired extends Component
{
    protected $listeners = ['expired'];    
    public function render()
    {
        return <<<'blade'
            <div>
                {{-- The best athlete wants his opponent at his best. --}}
            </div>
        blade;
    }
}

When Active button is clicked, it should load UserIsActive component. Same goes for other two.

I have been looking livewire doc for long, but unable to find how to make it happen. Thanks in advance.

Upvotes: 5

Views: 12068

Answers (3)

Peppermintology
Peppermintology

Reputation: 10210

I would wrap your components in a component container and use it to manage the visibility of your other components.

component-contaoner.blade.php

<div>
    <h4>Component Container</h4>

    {{-- this is your dynamic component --}}
    @livewire($component, key($key))

    <button wire:click="$emit('switch', 'active')">
        {{ __('Active') }}
    </button>

    <button wire:click="$emit('switch', 'pending')">
        {{ __('Pending') }}
    </button>

    <button wire:click="$emit('switch', 'expired')">
        {{ __('Expired') }}
    </button>
</div>

ComponentContainer.php

class ComponentContainer extends Component
{
    private $component = '';

    protected $listeners = [
        'switch'
    ];

    public function switch(string $component)
    {
        $this->component = $component;
    }

    public function mount(string $component = 'active')
    {
        $this->component = $component;
    }

    public function render()
    {
        return view('livewire.component-container', [
            'component' => $this->component,
            // key is required to force a refresh of the container component
            'key' => random_int(-999, 999),
        ]);
    }
}

Then you would use the component container as follows, passing in an optional component to show initially otherwise it defaults to active as set in the mount function.

@livewire('component-container')

You could put the buttons anywhere you want and use

$emitTo('container-component', 'switch', 'active')

I just put them inside the component-container for ease.

A nice thing about this approach is there are no if conditionals to manage and should you add more components to switch between, all you need to do is add another button somewhere with the relevant $emit.

Upvotes: 10

Prospero
Prospero

Reputation: 2352

Another way you can take is setting a self property and in blade make the corresponding livewire directive like if/else (assuming all is under full page component and the model binding refer a user model)

<button wire:click="showNested('active')">{{ __('Active') }}</button>
<button wire:click="showNested('pending')">{{ __('Pending') }}</button>
<button wire:click="showNested('expired')">{{ __('Expired') }}</button>

In view

@if($show == 'isActive')
   <livewire:user-is-active :active="$active"/>
@elseif($show == 'isPending')
   <livewire:user-is-pending :pending="$pending"/>
@elseif($show == 'isExpired')
   <livewire:user-is-expired :expired="$expired"/>
@endif

in component

public $active,$pending,$expired;
public $show = '';

public function render()
{
   if(!empty($this->show)){
      if($this->show == 'active')
         $this->active = User::where('status','active')->first();
      elseif(....)
        //......
   }
   return view(.....) //......;  
}

public function showNested($value)
{
   $this->show = $value;
}

Upvotes: 0

spekulatius
spekulatius

Reputation: 1499

One way to solve this is be adding all listeners to each of the components and switch a flag. Here on the example of UserIsExpired:

class UserIsExpired extends Component
{
    public $state = 'invisible';

    protected $listeners = [
        'active',
        'pending',
        'expired',
    ];

    public function active()
    {
        $this->state = 'invisible';
    }

    public function pending()
    {
        $this->state = 'invisible';
    }

    public function expired()
    {
        $this->state = 'visible';
    }

    public function render()
    {
        if ($this->state === 'invisble') {
            return '';
        }

        return <<<'blade'
            <div>
                {{-- The best athlete wants his opponent at his best. --}}
            </div>
        blade;
    }
}

For the initially visible component you probably want to set the default value for the state to visible.

Upvotes: 0

Related Questions