5

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.

3 Answers 3

10

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.

Sign up to request clarification or add additional context in comments.

7 Comments

Just came across this and it will be incredibly useful for something I'm trying to do. One minor nitpick: the random range for the key seems small to consistently guarantee a refresh. I assume it's just an illustrative number choice, but maybe something like random_int(PHP_INT_MIN, PHP_INT_MAX) will reduce issues for people that copy/paste without thinking about it. Maybe even using time (microseconds since epoch) could be more reliable than a random number.
@anjama The values were purely illustrational, feel free to adapt and use whatever mechanism is appropriate for your use case. Glad it could be of use to you though.
You can do without passing 'component' in the view if you set the $component property to public'.
This method somehow seems to trigger the "Multiple root elements detected." error in the console even though each component only has one element. Do you have any idea what could be causing this?
@Joseph Difficult to say without seeing your code. Perhaps ask a question?
|
0

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.

Comments

0

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;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.