Is it possible to update state of a single row in the records table?

I have the following toggle button on a table, and update Eloquent model based on the $state. The problem I am facing is that all of Toggle buttons on the table re-render right now and create some unpleasant experience. How can I limit this to only a single row?
Tables\Columns\ToggleColumn::make('availability')
->onColor('success')
->getStateUsing(fn (Product $record) => $record->status == ProductStatus::ACTIVE)
->updateStateUsing(function (Product $record, bool $state) {
$record->update([
'status' => $state
? ProductStatus::ACTIVE
: ProductStatus::PAUSED
]);
}),
Tables\Columns\ToggleColumn::make('availability')
->onColor('success')
->getStateUsing(fn (Product $record) => $record->status == ProductStatus::ACTIVE)
->updateStateUsing(function (Product $record, bool $state) {
$record->update([
'status' => $state
? ProductStatus::ACTIVE
: ProductStatus::PAUSED
]);
}),
30 Replies
Crylar
CrylarOP2y ago
By the way, I am noticing this side-effect only on a RecordsList without any edit page. Could this be related somehow?
Unknown User
Unknown User2y ago
Message Not Public
Sign In & Join Server To View
Dennis Koch
Dennis Koch2y ago
The problem I am facing is that all of Toggle buttons on the table re-render right now and create some unpleasant experience
Are you on the latest version? What does that look like? The one you changed should be disabled until there is a response from Livewire
Crylar
CrylarOP2y ago
Yeh I am on the latest version. I have two identical tables with the same code for toggle but one refreshes the whole table on toggle. The only difference is that I have just a single table page that has problems. 🙂
class ProductResource extends Resource
{
protected static ?string $model = Product::class;

protected static ?string $navigationIcon = 'heroicon-o-lightning-bolt';

protected static ?string $slug = 'arenas/pods/products';

protected static ?array $roles = ['super', 'waiter'];

public static function form(Form $form): Form
{
return $form
->schema([
//
]);
}

public static function table(Table $table): Table
{
return $table
->columns([

Tables\Columns\ToggleColumn::make('availability')
->onColor('success')
->getStateUsing(fn (Product $record) => $record->status == ProductStatus::ACTIVE)
->updateStateUsing(function (Product $record, bool $state, Pages\ListProducts $livewire) {
if ($record->status == ProductStatus::ACTIVE xor $state) {
$livewire->pod->pivotProducts()->updateOrCreate(
['product_id' => $record->id],
['status' => $state ? ProductStatus::ACTIVE : ProductStatus::PAUSED]
);
} else {
$livewire->pod->pivotProducts()
->where('product_id', $record->id)
->delete();
}
})
->extraHeaderAttributes(['style' => 'width: 150px']),
])
->filters([

])
->actions([

])
->bulkActions([

]);
}

public static function getRelations(): array
{
return [
//
];
}

public static function getPages(): array
{
return [
'index' => Pages\ListProducts::route('/'),
];
}
}
class ProductResource extends Resource
{
protected static ?string $model = Product::class;

protected static ?string $navigationIcon = 'heroicon-o-lightning-bolt';

protected static ?string $slug = 'arenas/pods/products';

protected static ?array $roles = ['super', 'waiter'];

public static function form(Form $form): Form
{
return $form
->schema([
//
]);
}

public static function table(Table $table): Table
{
return $table
->columns([

Tables\Columns\ToggleColumn::make('availability')
->onColor('success')
->getStateUsing(fn (Product $record) => $record->status == ProductStatus::ACTIVE)
->updateStateUsing(function (Product $record, bool $state, Pages\ListProducts $livewire) {
if ($record->status == ProductStatus::ACTIVE xor $state) {
$livewire->pod->pivotProducts()->updateOrCreate(
['product_id' => $record->id],
['status' => $state ? ProductStatus::ACTIVE : ProductStatus::PAUSED]
);
} else {
$livewire->pod->pivotProducts()
->where('product_id', $record->id)
->delete();
}
})
->extraHeaderAttributes(['style' => 'width: 150px']),
])
->filters([

])
->actions([

])
->bulkActions([

]);
}

public static function getRelations(): array
{
return [
//
];
}

public static function getPages(): array
{
return [
'index' => Pages\ListProducts::route('/'),
];
}
}
Here is what I got on the resource. 🙂
class ListProducts extends ListRecords
{
protected static string $resource = ProductResource::class;

// ---------------------------------------------------
// ---------------------------------------------------

/**
* Currently active pod id
*/
public ?int $podId = null;

/**
* List of available pods
*/
public array $availablePods = [];


// ---------------------------------------------------
// ---------------------------------------------------

public function __construct()
{
parent::__construct();

$this->queryString = array_merge($this->queryString, [
'podId' => ['as' => 'pod'],
]);
}

public function mount($parentRecord = null): void
{
parent::mount($parentRecord);

$arena = Arena::find(1);

$this->availablePods = $arena
->pods()
->get(['id', 'title', 'prefix', 'type'])
->toArray();

if ($this->podId === null) {
$this->podId = Arr::get($this->availablePods[0] ?? null, 'id');
}
}

public function getPodProperty(): ArenaPod
{
// first check if pod id is within available pods before requesting the model
if (! in_array($this->podId, Arr::pluck($this->availablePods, 'id'))) {
abort(404);
}

return ArenaPod::findOrFail($this->podId);
}
class ListProducts extends ListRecords
{
protected static string $resource = ProductResource::class;

// ---------------------------------------------------
// ---------------------------------------------------

/**
* Currently active pod id
*/
public ?int $podId = null;

/**
* List of available pods
*/
public array $availablePods = [];


// ---------------------------------------------------
// ---------------------------------------------------

public function __construct()
{
parent::__construct();

$this->queryString = array_merge($this->queryString, [
'podId' => ['as' => 'pod'],
]);
}

public function mount($parentRecord = null): void
{
parent::mount($parentRecord);

$arena = Arena::find(1);

$this->availablePods = $arena
->pods()
->get(['id', 'title', 'prefix', 'type'])
->toArray();

if ($this->podId === null) {
$this->podId = Arr::get($this->availablePods[0] ?? null, 'id');
}
}

public function getPodProperty(): ArenaPod
{
// first check if pod id is within available pods before requesting the model
if (! in_array($this->podId, Arr::pluck($this->availablePods, 'id'))) {
abort(404);
}

return ArenaPod::findOrFail($this->podId);
}
and here is the ListRecord. I have computed Eloquent model here, that I am update'ing. Could this be an issue? On the other table I am updating a given $record.
Dennis Koch
Dennis Koch2y ago
Do you have a unique key on the table?
Crylar
CrylarOP2y ago
How do I set one?
Crylar
CrylarOP2y ago
Crylar
CrylarOP2y ago
I see there is wire:key set but all of them are regenerated once I toggle
Dennis Koch
Dennis Koch2y ago
That seems fine 🤔
Crylar
CrylarOP2y ago
This is the table with keys where a single row is updated only.
Crylar
CrylarOP2y ago
There is no this random prefix on a key though.
Dennis Koch
Dennis Koch2y ago
Check the toggle itself. Does it have a unique key?
Crylar
CrylarOP2y ago
Crylar
CrylarOP2y ago
yep
Dennis Koch
Dennis Koch2y ago
Actually this should even be managed on the individual Alpine data. Can you show a video of the behaviour? Just checking we're talking about the same thing
Crylar
CrylarOP2y ago
Crylar
CrylarOP2y ago
I am showing both - the table that updates fully for some reason and the working one. 🙂
Dennis Koch
Dennis Koch2y ago
Okay, that's something different than I thought 😅 Currently it's not updating the selected record at all. Maybe that's your issue?
Crylar
CrylarOP2y ago
I am messing up with some other parts now so that's why but when it did the same effect too. I wonder what might cause the full refresh, and how can I control this. Could it be the computed property where I just get Eloquent model out of the ID might be a problem?
Dennis Koch
Dennis Koch2y ago
I wonder what might cause the full refresh, and how can I control this.
It's Livewire. It's always a full refresh with Livewire
Crylar
CrylarOP2y ago
On form we have something called afterStateUpdate or so, where you can then also tell what has changed but nothing on the table.
Dennis Koch
Dennis Koch2y ago
I don't really understand what you are doing with your mount()
Crylar
CrylarOP2y ago
I have this dropdown, that just sets queryString, and then I filter by the ID in my table query to show relevant results for the selection.
Crylar
CrylarOP2y ago
but these do not change during the toggle
Dennis Koch
Dennis Koch2y ago
Can I see the query/filter?
Crylar
CrylarOP2y ago
protected function getTableQuery(): Builder
{
return parent::getTableQuery()
->leftJoin('arena_pod_product', fn (JoinClause $join) =>
$join
->on('arena_pod_product.product_id', 'products.id')
->where('arena_pod_product.pod_id', $this->pod->id)
)
->join('categories', 'categories.id', 'products.category_id')
->where('categories.menu_id', $this->pod->menu_id)
->orderBy('products.order_to_display')
->orderBy('products.created_at')
->select([
'products.*',
'arena_pod_product.status as child_status'
]);
}
protected function getTableQuery(): Builder
{
return parent::getTableQuery()
->leftJoin('arena_pod_product', fn (JoinClause $join) =>
$join
->on('arena_pod_product.product_id', 'products.id')
->where('arena_pod_product.pod_id', $this->pod->id)
)
->join('categories', 'categories.id', 'products.category_id')
->where('categories.menu_id', $this->pod->menu_id)
->orderBy('products.order_to_display')
->orderBy('products.created_at')
->select([
'products.*',
'arena_pod_product.status as child_status'
]);
}
protected function getActions(): array
{
return [
Actions\SelectAction::make('podId')
->options(Arr::pluck($this->availablePods, 'title', 'id')),
];
}
protected function getActions(): array
{
return [
Actions\SelectAction::make('podId')
->options(Arr::pluck($this->availablePods, 'title', 'id')),
];
}
so through the action I set the podId, that later is computed to eloquent model when needed. I am not a fan of moving Eloquent model through livewire state. 😄 Any ideas @Dennis Koch, maybe you would put me on track where to start debugging to find cause. 🙂 Hm, I find that when I define what to select from a multi-join query then I see a full table refresh.
->select([
'products.*',
'arena_pod_product.status as child_status'
]);
->select([
'products.*',
'arena_pod_product.status as child_status'
]);
Dennis Koch
Dennis Koch2y ago
Not really. I am sorry.
Crylar
CrylarOP2y ago
public function __construct()
{
// parent::__construct();

$this->queryString = array_merge($this->queryString, [
'podId' => ['as' => 'pod'],
]);
}
public function __construct()
{
// parent::__construct();

$this->queryString = array_merge($this->queryString, [
'podId' => ['as' => 'pod'],
]);
}
@Dennis Koch I found out this parent::__ in the ListRecord was causing a full page refresh. Do you know a better method maybe on how to extended a queryString?
Dennis Koch
Dennis Koch2y ago
Not really. Is there a difference if you move the call to the bottom?
Crylar
CrylarOP2y ago
Nah, if parent::__construct(); is added anywhere it results into a full refresh. In the end, I do not need this contructor except I want to augment queryString when extending from ListRecords
protected $queryString = [
'isTableReordering' => ['except' => false],
'tableFilters',
'tableSortColumn' => ['except' => ''],
'tableSortDirection' => ['except' => ''],
'tableSearchQuery' => ['except' => ''],
];
protected $queryString = [
'isTableReordering' => ['except' => false],
'tableFilters',
'tableSortColumn' => ['except' => ''],
'tableSortDirection' => ['except' => ''],
'tableSearchQuery' => ['except' => ''],
];
I mean I want to add an additional item to this. I thought constructor was a proper way but not sure now. 🙂
Want results from more Discord servers?
Add your server