laravel-livewire-tables
laravel-livewire-tables copied to clipboard
[Bug]: The component class constructor doesn't seem to run when using ComponentColumn
What happened?
We're trying to use a Blade component as a column:
ComponentColumn::make('Fine Content', 'fine_weight_ozt')
->component('weight-with-conversions')
->attributes(fn ($value, $row, Column $column) => [
'weight' => new Weight($row->weighing_unit_type, $row->fine_weight),
]),
This does cause livewire-tables to attempt to render the component view, but it throws an "Undefined variable" exception because the public properties that are set in the constructor aren't present.
I don't believe the problem is in the component, because it works without issue when called in a view:
@php
$weight = new App\Data\Weight(1, 1)
@endphp
<x-weight-with-conversions :weight="$weight" />
So perhaps the constructor is not firing?
Here's the component class and view:
...
class WeightWithConversions extends Component
{
public Weight $weight;
public string $conversions;
/**
* Create a new component instance.
*/
public function __construct(Weight $weight)
{
$this->weight = $weight;
$this->conversions =
$weight->toString(WeighingUnitType::TroyOunce) . '<br/>' .
$weight->toString(WeighingUnitType::Gram) . '<br>' .
$weight->toString(WeighingUnitType::Pennyweight);
}
/**
* Get the view / contents that represent the component.
*/
public function render(): View|Closure|string
{
return view('components.weight-with-conversions');
}
}
View:
<button data-popover data-tippy-content="{!! $conversions !!}" class="underline-dotted">
{{ $weight->toString() }}
</button>
How to reproduce the bug
No response
Package Version
3.1.0
PHP Version
8.1.x
Laravel Version
10
Alpine Version
No response
Theme
None
Notes
No response
Error Message
No response
Thanks for raising, I'll have a look in a bit, just so I can confirm:
class WeightWithConversions extends Component
Is that: Livewire\Component or Illuminate\View\Component
I'm assuming the latter?
It's a Blade component.
<?php
namespace App\View\Components;
use Closure;
use Illuminate\Contracts\View\View;
use Illuminate\View\Component;
use App\Data\Weight;
use App\Enums\Item\WeighingUnitType;
class WeightWithConversions extends Component
{
public Weight $weight;
public string $conversions;
/**
* Create a new component instance.
*/
public function __construct(Weight $weight)
{
$this->weight = $weight;
$this->conversions =
$weight->toString(WeighingUnitType::TroyOunce) . '<br/>' .
$weight->toString(WeighingUnitType::Gram) . '<br>' .
$weight->toString(WeighingUnitType::Pennyweight);
}
/**
* Get the view / contents that represent the component.
*/
public function render(): View|Closure|string
{
return view('components.weight-with-conversions');
}
}
Perfect, thanks, I'll take a look shortly.
Just having a look at why this isn't working for you, as I have a ComponentColumn working in my test environment. Will see if I can figure out why it works for me and not for you!
Thanks for looking into this!
I see one potential issue (weighing_unit_type has a cast), I'll investigate and update this.
I looked at it, still no luck. The Weight
constructor throws an exception if supplied invalid arguments.
I put a dd()
in the component constructor, but I don't get a dump when I view the page with the table. I move the dd()
to the component view, and I do.
Can you show me your working test?
I'll have to share it tomorrow, as I'm tuckered out now: See Here for why! That should all get merged in, possibly tomorrow as a new release. But then I will definitely share my working example!
Although glancing at it, I think mine may be an anonymous component, which would explain a lot as to why mine is working and yours isn't.
I'll pop the code open tomorrow to see if I can figure out what's going on there.
Although glancing at it, I think mine may be an anonymous component, which would explain a lot as to why mine is working and yours isn't.
Yes I think this is the difference.
Hi, wanted to see if you've had any chance to take a look at this. Thank you!
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
Don't close :)
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
Is this something that can be looked at?
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
This is still an issue
Apologies, stalebot gets a bit over-eager (I have no control over that!)
I'll do my best to take a look at this over the next week (so expect an update by the end of the month).
I have tinkered a little, and have got this resolved in my local environment (I've added a new Column type to avoid any conflicts with existing code).
If you want to give it a whirl, ping me on Discord and I'll let you know what to do to give it a test (would appreciate a real-world test tbh)
Please do reach out if you want to test this, as otherwise I'll throw it into the next version, and it may not do what you want!
Hello! I don't really use Discord, could you post some code here? I could temporarily modify my local vendor files and test it.
To test this, all you need is a component which has a class constructor that sets some variable. Then reference that variable in the component view to make sure it's available.
Okay, replicated, and have a fix for it, it'll come out as a new ViewComponentColumn type.
I will need to write:
- Docs (Completed)
- Tests (In Progress)
Before this can be released. Estimate is by end of the month
Please do feel free to test in the meantime!
There's a new ViewComponentColumn which fixes this issue. Let me know if you have any issues. Docs should be on the site shortly.
Docs are released for ViewComponentColumn which should fix this! Please reply if issue persists.
Thank you! I'll try to test this weekend and let you know how it works.
I was able to try this out but am still having trouble.
Here's the latest thing I've tried:
ComponentColumn::make('Fine Content', 'fine_weight_ozt')
->component('weight-with-conversions')
->attributes(fn ($value, $row, Column $column) => [
'weight' => new Weight($row->weighing_unit_type->value, $row->fine_weight),
])
I've also tried ViewComponentColumn
with the same result. The docs say to use ComponentColumn
, but the actual class name is ViewComponentColumn
.
When I use this, I get an exception: https://gyazo.com/de0a72a769ca3b0bab45ec97b2e2f417
It doesn't seem like the exception itself is meaningful, as the underlying issue is that for whatever reason, the weight
property we're passing into this component column is used as the value for the heading
property in a completely different Blade component, which lives on the same view that we're calling the Livewire table from.
@nathan-io - Can you share your weight-with-conversions component if possible?
I'm assuming that retail-catalog is the other component that seems to be interfered with?
Is the retail-catalog Component a child component of the same parent as the table? (i.e.
"VIew Things" has a Data Table Livewire Component and a Retail Catalog View Component
A broken DOM can also cause properties to be leaked between different components
I found a dd()
I had left in the component class. I haven't worked on this in a few months so I totally forgot about that.
Unfortunately that wasn't the issue, but it at least explains why it didn't matter if I used ViewComponentColumn
or ComponentColumn
(have to use the latter).
I'm now getting an Undefined variable $conversions
exception in the component view weight-with-conversions.blade.php
.
$conversions
is a public property set in the component class WeightWithConversions
, so it should be available there.
When I tried {!! $conversions ?? '' !!}
, the exception changed to Undefined variable $weight
.
$weight
is another public property set in the component class.
The component works fine when we just call it like:
<x-weight-with-conversions :weight="$itemWeight"/>
So to test, you probably don't need App\Data\Weight
or App\Enums\Item\WeighingUnitType
. You can just set the properties to some arbitrary value, as I think the real issue is that the component class still isn't firing and making its properties available to the view.
Usage
package v3.3.4
ComponentColumn::make('Fine Content', 'fine_weight_ozt')
->component('weight-with-conversions')
->attributes(fn ($value, $row, Column $column) => [
'weight' => new Weight($row->weighing_unit_type->value, $row->fine_weight),
// 'conversions' => '' // The constructor should set this for us but still got `Undefined variable $conversions` when including this as an experiment
])->sortable(
fn(Builder $query, string $direction) => $query->orderBy('fine_weight_ozt', $direction)
),
Component class
<?php
namespace App\View\Components;
use Closure;
use Illuminate\Contracts\View\View;
use Illuminate\View\Component;
use App\Data\Weight;
use App\Enums\Item\WeighingUnitType;
class WeightWithConversions extends Component
{
public Weight $weight;
public string $conversions;
/**
* Create a new component instance.
*/
public function __construct(Weight $weight)
{
$this->weight = $weight;
$this->conversions =
$weight->toString(WeighingUnitType::TroyOunce) . '<br/>' .
$weight->toString(WeighingUnitType::Gram) . '<br>' .
$weight->toString(WeighingUnitType::Pennyweight);
}
/**
* Get the view / contents that represent the component.
*/
public function render(): View|Closure|string
{
return view('components.weight-with-conversions');
}
}
The component view
<button data-popover data-tippy-content="{!! $conversions !!}" class="underline-dotted">
{{ $weight->toString() }}
</button>
Hmmm, do all of your rows have both $row->weighing_unit_type->value and $row->fine_weight
Otherwise you may not be able to create a Weight, which may result in the error you're experiencing, not saying that's definitely the issue, but possible
Unfortunately that's not the issue. weighing_unit_type
and fine_weight
aren't nullable, and all of the values for both columns are set and validated during seeding.
It really just seems like the component class just isn't being invoked, and thus it isn't making data available to the view.
I think I may have just figured out why, just testing to see if it's the case!
So, I've made a tweak, and think it should be fixed for the next version. To demonstrate what I'm doing to test:
My test column looks like this, just using notes_count as it's a value I already have in a test table.
ViewComponentColumn::make('Weight', 'name')
->component('test-weight')
->attributes(fn ($value, $row, Column $column) => [
'weight' => new Weight(($row->notes_count ?? 10)*rand(5,240))
]),
test-weight View Component
<?php
namespace App\View\Components;
class TestWeight extends \Illuminate\View\Component
{
public ?\App\Weight $weight;
public function __construct(\App\Weight $weight)
{
$this->weight = $weight;
}
/**
* Get the view / contents that represent the component.
*/
public function render(): \Illuminate\Contracts\View\View|Closure|string
{
return view('test-weight');
}
}
test-weight.blade.php
<div>
{{ $weight->getConversion() ?? 'Unknown'}}
</div>
Weight Class
<?php
namespace App;
class Weight
{
public int $grams;
public int $ounces;
public function __construct(int $grams)
{
$this->grams = $grams;
$this->setOunces();
}
public function setOunces()
{
$this->ounces = $this->grams / 28.3495;
}
public function getConversion(): string
{
return $this->grams."g, ".$this->ounces."oz";
}
}
It seems to be working:
3.4.0 is out now
This fix will be in 3.4.1 which I'll release momentarily