Livewire Flickity Conflict/Issue - laravel

I am having issues rendering a dynamic slider built using Flickity. I have a CRUD where I add/remove slider images to the database and load them instantly live on the page. I have the following code setup, when I add a new slider image using addPhoto() method, it is added but does not load on the page until I refresh the page.
Livewire and Flickity conflict with each other. If i remove wire:ignore from the blade template it loads the newly added photo but messes with the Flickity slider (of course livewire manipulates the DOM).
If i keep wire:ignore inside the blade template it does not load the newly added photo until i manually reload the page. I also tried the '$refresh' via listener but no luck.
Is there any solution or workaround for this issue?
class ProfileEdit extends Component
{
public $photos;
public function mount()
{
$this->loadPhotos();
}
public addPhoto()
{
// Add a new photo to database
// And finally load photos again to get latest data
$this->loadPhotos();
}
protected function loadPhotos()
{
$this->photos = load photos from database
}
public function render()
{
return view('livewire.carousel');
}
}
<div>
<div wire:ignore x-data="carousel()" x-init="init()" x-ref="carousel">
#foreach ($photos as $photo)
<div wire:key="{{ $loop->index }}">
</div>
#endforeach
</div>
<script>
function carousel() {
return {
init() {
var flkty = new Flickity(this.$refs.carousel, {
//
});
},
}
}
</script>
</div>

mount() is only ever called when the component is first mounted and will not be called again even when the component is refreshed or rerendered.
class ProfileEdit extends Component
{
public addPhoto()
{
// Add a new photo to database
}
protected function loadPhotos()
{
$photos = load photos from database
return $photos;
}
public function render()
{
return view('livewire.carousel', [
'photos' => $this->loadPhotos(),
]);
}
}

Related

Livewire component not updating

I have a simple component:
<input type="text" wire:model="search">
{{ $search }}
I load this like this:
#livewire('searchbar')
And this is my class:
class Searchbar extends Component
{
public $search;
public $results = [];
public function updatedSearch()
{
info($this->search);
$this->results = Client::search($this->search)->get();
}
public function render()
{
return view('livewire.searchbar', [
'results' => $this->results,
]);
}
}
When I type into the input field, the info() logs the correct value but in the view, the the {{ $search }} gets not updated. When I use protected $queryString = ['search']; the URL get's updated correctly as well and if I would use dd() instead of info() I'd see the view updating with the dd.
Why is the $search not updating in the view?
Wrap your component. Make sure your Blade view only has ONE root element.
<div>
<input type="text" wire:model="search">
{{ $search }}
</div>
First thing first, inside livewire render, you do not need to send variables.
This will be sufficient.
public function render()
{
return view('livewire.searchbar');
}
You already declared $results as public variable, just like $search.
Livewire will know when the content of those variables are updated.
And now please try again.
$search should be automatically updated based on any text you inserted in input text with wire:model attribute.

nouislider with livewire how to get values

How can i get the values from nouislider in livewire? So i can compare the min and max price. I don't see how i can get the values. It has 2 handles a min and max price.
<div wire:ignore id="slider-margin"></div>
<div class="d-flex">
<div wire:model="min_price" class="me-5" id="slider-margin-value-min" aria-valuenow="min_price"></div>
<div wire:model="max_price" id="slider-margin-value-max" aria-valuemax="max_price"></div>
</div>
class Shop extends Component
{
use WithPagination;
protected $paginationTheme = 'bootstrap';
public $min_price;
public $max_price;
public function render()
{
$this->products = Product::query()
->wherebetween('price',[$this->min_price,$this->max_price])->paginate(9);
return view('livewire.shop',[
'products'=>$this->products
]);
}
}
try this code:
<div wire:ignore id="slider-margin"></div>
<div class="d-flex">
<div wire:click="setMin({{ $min_price }})" class="me-5" id="slider-margin-value-min" aria-valuenow="min_price"></div>
<div wire:click="setMax({{ $max_price }})" id="slider-margin-value-max" aria-valuemax="max_price"></div>
</div>
and:
class Shop extends Component
{
use WithPagination;
protected $paginationTheme = 'bootstrap';
public $min_price;
public $max_price;
public function setMax($max_price)
{
$this->max_price= $max_price;
}
public function setMin($min_price)
{
$this->min_price = $min_price;
}
public function mount()
{
$this->products = Product::query()
->wherebetween('price',[$this->min_price,$this->max_price])->paginate(9);
}
}
A div is no input, so a wire:model won't do anything on it. noUiSlider builds HTML after page load, so to be able to use it for variables in Livewire, you will have to use JavaScript. You can listen for the events and use the global Livewire JS object to emit changes to your component
So, an example of how this might work:
let slider = document.getElementById('slider-margin');
slider.noUiSlider.on('update', function (values) {
Livewire.emitTo('shop', 'sliderUpdated', values);
});
Then in your component, you can do something with the passed values:
function sliderUpdated(array $values)
{
$this->min_price = \Illuminate\Support\Arr::first($values);
}
Ensure to take a good read on the event docs, as I am not 100% sure above code will function straight away, but I think this will at least get you in the right direction!

Laravel Accessing Attributes & Slots Within Component Classes

I have a question about Laravel - Documentation - Accessing Attributes & Slots Within Component Classes
I read this section, and did some experiments. What I've found is that the closure only can return string but component.view, and what it returns would overwrite what is defined on the component view, which leads me to a question What use case is this feature for?
Could anyone make some examples of using this feature for me?
Anyone could help me with it will be so much appreciated.
If the string matches an existing view then it will render that view and not overwrite it. You can return an existing view as follows:
// app/View/Components/Post.php
public function render()
{
return function (array $data) {
// $data['componentName'];
// $data['attributes'];
// $data['slot'];
return 'components.post'; // /resources/views/components/post.blade.php
};
}
// views/components/post.blade.php
<div class="item">
<div class="title">
New Post
</div>
<div class="content">
<p>Content post...</p>
</div>
</div>
Example:
// component class
class Link extends Component
{
public $path = "";
public function __construct()
{
$this->path = request()->path();
}
public function render()
{
return function (array $data) {
if(isset($data['attributes']['href'])) {
$data['attributes']["link"] = $data['attributes']['href'];
if ($data['attributes']['href'] == "/".$this->path) {
$data['attributes']['active'] = $data['attributes']['class']." link-active-class";
} else {
$data['attributes']['active'] = $data['attributes']['class'];
}
}
return 'components.commons.link';
};
}
}
// component view
<a href="{{ $attributes['link'] }}" class="{{ $attributes['active'] }}">
{{ $slot }}
</a>
Usage:
<x-commons.link href="/module/news" class="primary-action">
<i class="fas fa-newspaper"></i> News
</x-commons.link>

Sending laravel livewire event from child to parent gives fingerprint error

Im getting this error when sending an event from child to parent container. I am using wire:key as recommended but get the JS error Cannot read property 'fingerprint' of null. Any ideas what I am doing wrong? Please see example below.
Container
class Container extends Component
{
public $listeners = [
'submit'
];
public function render()
{
return view('livewire.container');
}
public function submit()
{
//dd("The child says wow - it works!");
}
}
with view
<div>
<h1>Im the container</h1>
#foreach([1,2,3] as $nbr)
#livewire('child', compact('nbr'))
#endforeach
</div>
Child
class Child extends Component
{
public $nbr;
public function mount($nbr)
{
$this->nbr = $nbr;
}
public function render()
{
return view('livewire.child');
}
public function submit()
{
$this->emit('submit', 'wow!');
}
}
with view
<div wire:key="{{ $nbr }}">
Hey im a child
<button wire:click="submit">submit</button>
</div>
When defining Livewire components in a loop, you need to give them a key, so Livewire can keep track of each individual component.
<div>
<h1>Im the container</h1>
#foreach([1,2,3] as $key=>$nbr)
#livewire('child', compact('nbr'), key($nbr))
#endforeach
</div>
This is done on the component, not on the root-element in the view, meaning that its incorrectly placed with wire:key="{{ $nbr }}" being in your child-view.

check if data passed from laravel to vue exists

Sometimes I need to pass data from my controller to the view and sometimes I dont!
But when theres no data passed I get this error:
Undefined variable: tutorial (View:
/var/www/html/resources/views/home.blade.php)
No data example:
public function index()
{
return view('home');
}
Containg data example
public function tutorial()
{
return view('home', ['tutorial' => 'Welcome to myproject, lets get started!!!']);
}
Blade:
beforeMount()
{
let tutorial = {{ var_export($tutorial) }} // ERROR!!
if (tutorial) {
return
}
this.reloadData()
}
I think I can do this way in my index function:
public function index()
{
return view('home', ['tutorial' => []);
}
But its just gross!! Theres anyway to check if data passed from laravel exists in my vue??
Because you cannot just put the Laravel variable in js.
Options to solving the problem included:
Performing an api request for the data using the application
component once it had been mounted.
Attaching the data into the
Javascript context using the blade template.
You can do some thing like this:
<Home tutorial="{{ $tutorial }}"> <!-- make tutorial become to props -->
</Home>
And in your view component:
Vue.component('Home', {
props: [
{
name: 'tutorial', // add this props
default: '',
}
],
beforeMount()
{
if (this.tutorial) { // use like this.tutorial
return
}
this.reloadData()
}
});

Resources