F
Filament3mo ago
Mook

Bulk actions which is only visible when 2 rows are selected

I'm trying to make a bulk action appear only when 2 table rows are selected. I'm using the visible function to try and achieve this: ->visible(fn ($livewire) => $livewire->selectedTableRecords == 2) But this livewire array always seems to be empty no matter how many rows are selected? I'm stuck, please help.
8 Replies
Kane G
Kane G3mo ago
Have you tried just accessing the collection?
->visible(fn (Collection $selectedRecords) => $selectedRecords->count() == 2)
->visible(fn (Collection $selectedRecords) => $selectedRecords->count() == 2)
awcodes
awcodes3mo ago
I’m sure I’m wrong but seems like
count($livewire->getSelectedTableRecords())
count($livewire->getSelectedTableRecords())
Might be what you are after.
Mook
MookOP2mo ago
Thanks for the suggestions, I just found this from Dan: https://discord.com/channels/883083792112300104/1088344527745585182 looks like the PHP array is never updated and is only stored in the JS. With $selectedRecords the code errors as it is always null when first called. ($selectedRecords) must be of type Illuminate\Database\Eloquent\Collection, null given Is it possible to access the Filament JS?
Travis
Travis2mo ago
I'm looking for a solution to this, too. It seems difficult to get the selected records upon a change in row selections. I'm going to take a close(r) look at the built-in bulk actions to see if they offer anything like this (like restoring/trashing deleted resources).
Mook
MookOP2mo ago
Let me know if you find anything. I’ve hit a brick wall with this and can not find a solution
Travis
Travis2mo ago
I found a solution, but not the one I was looking for. I basically did the same thing as the built-in force delete and restore actions do, which relies on the presence of a filter. So, in my case, I have a multi-select filter with different states: pending, approved, and rejected. I examine the filter and only allow the various actions, depending. It's only when the pending filter is present that the approve/reject actions are enabled, for example. Ideally, I would get all of the selected items and examine them to decide what should be allowed, but I haven't figured out how to do that yet, if it's even possible. 🤷‍♂️
Mook
MookOP2mo ago
Can you share your bulk action code?
Travis
Travis2mo ago
Sure. Keep in mind that this is using a multi-select filter, where the user can choose any combination of three states: pending, approved, and rejected. I changed a few names and added some comments to try to help.
<?php

namespace App\Filament\Resources\MyResource\TableActions;

use App\Enums\MyResourceState;
use Filament\Tables\Actions\BulkAction;
use Filament\Tables\Contracts\HasTable;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;

class ApproveBulkAction extends BulkAction
{
public static function getDefaultName(): ?string
{
return 'approve-bulk-resource';
}

protected function setUp(): void
{
parent::setUp();

$this->label('Approve')
->icon('heroicon-o-hand-thumb-up')
->visible($this->determineVisibility(...))
->action($this->approveAction(...));
}

private function determineVisibility(HasTable $livewire): bool
{
// get the state of the state filter
$stateFilterState = $livewire->getTableFilterState('state') ?? [];

// if the 'values' array key does not exist, return false (invisible)
if (! array_key_exists('values', $stateFilterState)) {
return false;
}

// get the state filter state values
$values = $stateFilterState['values'];

// if more/less than one are selected, return false (invisible)
if (count($values) !== 1) {
return false;
}

// return true (visible) if the 'pending' state filter is selected/enabled, false otherwise (invisible)
return Arr::first($values) === MyResourceState::PENDING->value;
}

private function approveAction(Collection $records): void
{
$records->each->approve();
}
}
<?php

namespace App\Filament\Resources\MyResource\TableActions;

use App\Enums\MyResourceState;
use Filament\Tables\Actions\BulkAction;
use Filament\Tables\Contracts\HasTable;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;

class ApproveBulkAction extends BulkAction
{
public static function getDefaultName(): ?string
{
return 'approve-bulk-resource';
}

protected function setUp(): void
{
parent::setUp();

$this->label('Approve')
->icon('heroicon-o-hand-thumb-up')
->visible($this->determineVisibility(...))
->action($this->approveAction(...));
}

private function determineVisibility(HasTable $livewire): bool
{
// get the state of the state filter
$stateFilterState = $livewire->getTableFilterState('state') ?? [];

// if the 'values' array key does not exist, return false (invisible)
if (! array_key_exists('values', $stateFilterState)) {
return false;
}

// get the state filter state values
$values = $stateFilterState['values'];

// if more/less than one are selected, return false (invisible)
if (count($values) !== 1) {
return false;
}

// return true (visible) if the 'pending' state filter is selected/enabled, false otherwise (invisible)
return Arr::first($values) === MyResourceState::PENDING->value;
}

private function approveAction(Collection $records): void
{
$records->each->approve();
}
}
It gets the table's filter state for a filter named, "state", which may be confusing at first. 😅 Then, it looks to see if it has "values". If you have a normal select filter, then this would be "value", I believe. Then, for my logic, I want to check to see if only the "pending" state is selected. If so, then this is when I want to make the "approve" bulk action visible. You may have different logic. For the bulk "revert" action, for example, I want it to be visible if the user has selected either the "approved" state or the "rejected" state. Let me know if you have any questions.
Want results from more Discord servers?
Add your server