Hello!
I have a Livewire 3 project where I use simple lightbox js plugin <script src="fslightbox.js"> </script> added before end of the <body> tag. I also have simple filtering on this page and everything works fine when the page is first loaded and after first component update, but stops working after that.
Does anyone maybe have some suggestions how can I solve this?
Any help would be appreciated.
Many thanks.
You may need to exclude Livewire from re-rendering the component. Not tested, but may help:
<div wire:ignore>
// Your plugin stuff here
</div>
many thanks for quick reply Alex,
I've tried that but no luck. Code is fairly simple so what am i doing wrong here?
@foreach ($items as $item)
<div wire:key="{{$item->id}}">
<div wire:ignore>
<a data-fslightbox="{{ $item->id }}" href="#">
{{ $item->name }}
</a>
</div>
</div>
@endforeach
attribute "data-fslightbox" is the only thing realted to the plugin...
Let me pull together a local example for you and test it. Will let you know shortly!
Can I just ask what this app does so I can re-create a similar example?
Does it list through items, and clicking on each item opens it's own lightbox showing images?
Yes - lts a list of imges clicking on each opens its own lightsbox.
But there is also have a simple function that filters $items based on 'type':
// App\Livewire\Items.php
public $items;
public function mount(){
$this->items = Item::all();
}
public function filterByType($type){
$this->items = Item::where('type', $type)->get();
}
public function render() {
return view('livewire.items.index', [
'items' => $this->items,
]}
As i mentioned before It works fine when the page is first loaded and then when I start to filter the results based on type it works after first component update and then lightbox doesnt work anymore.
My previous view code isnt probably clear enough so this is slightly updated version:
@foreach ($items as $item)
<div wire:key="{{$item->id}}">
<div wire:ignore>
<a data-fslightbox="{{ $item->id }}" href="{{ Storage::disk('items')->url($item->photo)}}">
<img src = "{{ Storage::disk('items')->url($item->photo)}}">
</a>
</div>
</div>
@endforeach
Hope it makes sense.
I've put together a full working example that'll hopefully help clear this up. It may work slightly differently to how you're doing things, but the general idea:
Item
model that contains many photos$index
, so ignore thatHere are all the relevant files:
app.blade.php
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>{{ config('app.name', 'Laravel') }}</title>
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.bunny.net">
<link href="https://fonts.bunny.net/css?family=figtree:400,500,600&display=swap" rel="stylesheet" />
<!-- Scripts -->
@vite(['resources/css/app.css', 'resources/js/app.js'])
</head>
<body class="font-sans antialiased">
<div class="min-h-screen bg-gray-100">
<livewire:layout.navigation />
<!-- Page Heading -->
@if (isset($header))
<header class="bg-white shadow">
<div class="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
{{ $header }}
</div>
</header>
@endif
<!-- Page Content -->
<main>
{{ $slot }}
</main>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fslightbox/3.0.9/index.js"></script>
</body>
</html>
lightbox.blade.php
(the Livewire component template)
<div>
@foreach($items as $item)
<div class="flex flex-col gap-2">
{{--This is the item's preview image--}}
<a data-fslightbox="gallery" href="https://picsum.photos/1000?{{ $item->id }}">
<img src="https://picsum.photos/100?{{ $item->id }}" alt="Random image">
</a>
{{--These are the images associated with the item for the lightbox--}}
@foreach (range(1,3) as $index => $image)
<div wire:key="{{ $index }}">
<a class="hidden" data-fslightbox="gallery" href="https://picsum.photos/1000?{{ $index }}">
Image #{{ $index }}
</a>
</div>
@endforeach
</div>
@endforeach
<button wire:click="update">Update order</button>
</div>
Livewire.php
(the Livewire component)
<?php
namespace App\Livewire;
use App\Models\Item;
use Illuminate\Support\Collection;
use Livewire\Attributes\Layout;
use Livewire\Component;
class Lightbox extends Component
{
public Collection $items;
public function mount()
{
$this->update();
}
public function update()
{
$this->items = Item::inRandomOrder()->get();
}
#[Layout('layouts.app')]
public function render()
{
return view('livewire.lightbox', [
'items' => $this->items
]);
}
}
The update
method just re-renders the component with a new order for the $items
, thus completely re-rendering the lightbox.
This is working fine with this example I've pulled together, even after randomly ordering (in your case filtering) the items.
If you're still stuck, here to help!
many thanks Alex,
your code works like a charm, but when I tried quickly adapt mine then it still stops working (although this time after 3rd, 4th update - weird). I willl completely rewrite my component using your code as a tempolate and let you know.
many thanks for your time
Happy to help! Let me know how it goes and pop back here. We’ll sort this!
Checked again. Your code works every time but when I change update() function to include "where" clause i.e
public function update(){
$type = rand(1, 5);
$this->items = Item::where('type', $type)->get();
}
it stops working after first component update.
That's strange. By stops working, do you mean the lightbox just won't open? Any errors in the console?
morning Alex,
by "stops" I mean after I click a photo - browser redirects me to this photo instead of opening a lightbox,
No console errors.
I also noticed that when I hardcode type
$this->items = Item::where('type', 1)->get();
it works fine every time compnent is updated.
weird.
That's strange, I've adjusted my code to match and I can't re-create.
Could you paste in your full Livewire component and template so I can copy over to my project locally?
tested with fresh laravel install - same problem however sometimes component can refresh even 6-8 times before problem occurs
plugin script is included before end of the <body> tag.
lightbox.blade.php
:
<div class="max-w-7xl mx-auto px-8 mt-8 flex space-x-12">
<div >
<h1> FILTER</h1>
<div class="flex flex-col gap-2 mt-4">
<button wire:click="filterByType(1)">
type 1
</button>
<button wire:click="filterByType(2)">
type 2
</button>
<button wire:click="filterByType(3)">
type 3
</button>
<button wire:click="filterByType(4)">
type 4
</button>
<button wire:click="filterByType(5)">
type 5
</button>
</div>
</div>
<div>
@foreach($items as $item)
<div class="flex flex-col gap-2">
{{--This is the items preview image--}}
<a data-fslightbox="gallery" href="https://picsum.photos/1000?{{ $item->id }}">
<img src="https://picsum.photos/100?{{ $item->id }}" alt="Random image">
</a>
{{--These are the images associated with the item for the lightbox--}}
@foreach (range(1,3) as $index => $image)
<div wire:key="{{ $index }}">
<a class="hidden" data-fslightbox="gallery" href="https://picsum.photos/1000?{{ $index }}">
Image #{{ $index }}
</a>
</div>
@endforeach
</div>
@endforeach
</div>
</div>
Lightbox.php
:
namespace App\Livewire;
use Livewire\Component;
use Livewire\Attributes\Layout;
use Illuminate\Support\Collection;
use App\Models\Item;
class Lightbox extends Component
{
public Collection $items;
public function mount()
{
$this->items = Item::all();
}
public function filterByType($type)
{
$this->items = Item::where('type', $type)->get();
}
#[Layout('layouts.app')]
public function render()
{
return view(
'livewire.lightbox',
[
'items' => $this->items
]
);
}
}
Just swapped over to use exactly these and not seeing an issue even after filtering multiple times.
With the fslightbox script you're including, is this from local or from a CDN?
tried both.
Thank you for your efforts Alex. Dont waste more of your time on this. You were more than helpful.
I've just tried another lightbox library - Photoswipe and I have exactly the same issue. Tried on two diffrent PC's, three different browsers.
Really strange. Just one of those things I guess....
Anyway - thanks again.
One last question :
Is there a reason why you are not including wire:key in your @foreach($items as $item) loop?
Happy to help! I'll continue to play around with it, will be super helpful for future knowledge what the issue was.
Let me know if you solve it in the meantime.
Is there a reason why you are not including wire:key in your @foreach($items as $item) loop?
Just forgot :)
So,
I've noticed that it works fine when the number of filtered results is the same, thats why Alex's code always works - it returns the same number of results , just in random order. And thats why my code works fine for a random number of updates (works as long as number of filtered results is the same as previous).
So it has something to do with number of gallery insatnces on the page.
In documentation i found refreshFsLightbox() function that incorporates changes to <a> elements but it didnt help in my case.
So i tried and dynamically load the fslightbox script on every component update (via dispatch and window.addEventListener() ).
I dont belive this is the best solution but it works in my case.
hope this helps.
Well, I'm glad you found one solution! The tricky thing with scripts pulled in like this and not used as an Alpine directive/plugin, they can be temperamental.
As always, let me know if you need anything else :)