Sending laravel livewire event from child to parent gives fingerprint error - laravel

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.

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.

Livewire Flickity Conflict/Issue

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(),
]);
}
}

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 Livewire - How to force child component refresh

I'd like to refresh the child component when the parent is updated and only the child component of this component should update, because I'd like to reuse the child component somewhere else in the page. The components structure is the following.
UserShow.php
class UserShow extends Component
{
public User $user;
public int $userId;
public function mount() {
$this->user = User::first();
}
public function updatedUserId() {
$this->user = User::find($this->userId);
}
public function render() {
return view('livewire.user-show');
}
}
<div>
<select wire:model="userId">
<option value="1">Matteo</option>
<option value="2">Giulia</option>
<option value="3">Mattia</option>
</select>
<div>
Name: {{ $user->name }}
#livewire('user-show-email', ['user' => $user])
</div>
</div>
UserShowEmail.php
class UserShowEmail extends Component
{
/** #var User */
public $user;
public function render() {
return view('livewire.user-show-email');
}
}
<div>
Email: {{ $user->email }}
</div>
When the user in UserShow is changed via updatedUserId() method, I'd like that $user prop in UserShowEmail will change accordingly. By reading the docs it seems that Livewire is not able to automatically refresh the child component.
I wonder if there's a workaround, or something that allow me to refresh the child component when parent changed.
Thanks!
If you don't mind updating the whole child component, this would be one way of doing it:
There's a trick that always work for me when I want to make a child
Livewire component "reactive". In your parent livewire component you
use a timestamp for the key attribute:
<livewire:child-component key="{{ now() }}" :my-prop="$myModel" />
The tricky part is the now() function in key prop. This component now
will always be updated when the parent is updated.
In your parent component:
<div>
...
<div>
Name: {{ $user->name }}
<livewire:user-show-email key="{{ now() }}" :user="$user" />
</div>
</div>
You can read more about it here and here.

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>

Resources