F
Filament2mo ago
nowak

How to trigger a custom action modal directly from a TextInputColumn updateStateUsing?

I have a TextInputColumn on a table for my resources route_id attribute. When this is changed to a route_id that does not exist, I want to prompt the user with an action modal, where they can create a new route record, which would update the route_id column with the id of the newly created route. I am trying to do this:
TextInputColumn::make('route_id')
->rules(['numeric', 'integer', 'min:1'])
->extraAttributes(['style' => 'min-width:60px'])
->updateStateUsing(function ($state, $record) {
if ($state > 1 || $state !== null) {
$route = Route::find($state);

if (!$route) {
// Trigger the createRoute action if the route does not exist
return Action::make('createRoute')
->form([
TextInput::make('name')->required()->maxLength(255),
ColorPicker::make('color')->required(),
])
->action(function (array $data) use ($state) {
$data['id'] = $state;
Route::create($data);
})
->modalHeading('Create Route')
->modalSubmitActionLabel('Create')
->modalWidth('lg')
->visible(fn () => true);
}
$groupOrderUpdater = app(UpdatesGroupOrders::class);
$groupOrderUpdater->update($record, ['route_id' => $state]);
}
return $record->route_id;
}),
TextInputColumn::make('route_id')
->rules(['numeric', 'integer', 'min:1'])
->extraAttributes(['style' => 'min-width:60px'])
->updateStateUsing(function ($state, $record) {
if ($state > 1 || $state !== null) {
$route = Route::find($state);

if (!$route) {
// Trigger the createRoute action if the route does not exist
return Action::make('createRoute')
->form([
TextInput::make('name')->required()->maxLength(255),
ColorPicker::make('color')->required(),
])
->action(function (array $data) use ($state) {
$data['id'] = $state;
Route::create($data);
})
->modalHeading('Create Route')
->modalSubmitActionLabel('Create')
->modalWidth('lg')
->visible(fn () => true);
}
$groupOrderUpdater = app(UpdatesGroupOrders::class);
$groupOrderUpdater->update($record, ['route_id' => $state]);
}
return $record->route_id;
}),
But the action modal does not get triggered at all, what am I doing wrong?
Solution:
We cannot register an Action here, I guess. I created a header action as a workaround. Is it elegant? No, but it might work for your case. ListPage ```php protected function getHeaderActions(): array...
Jump to solution
32 Replies
karpadiem
karpadiem4w ago
Im also struggling to get this working for a checkboxColumn. Were you able to figure it out @nowak ?
nowak
nowak4w ago
Not yet, no :/
karpadiem
karpadiem4w ago
I even added a log::info to the function and it looks like it doesn’t get hit at all.
LeandroFerreira
Hum.. maybe registering a custom action in a widget and dispatching with arguments 🤔
awcodes
awcodes4w ago
You can’t ‘make’ the action on demand. It’s too late in the lifecycle. It has to be registered during component creation. Then you can dispatch a mountAction(), mountTableAction() or mountFormComponentAction() via livewire with the action name and arguments. Will probably need a custom column that extends TextColumn to register the action during setUp()
karpadiem
karpadiem4w ago
From what I’ve been reading, it seems like ->updateStateUsing() should be the way to modify how the state is stored right?
awcodes
awcodes4w ago
Yes, but actions and state are 2 very different things.
karpadiem
karpadiem4w ago
I see that. In the code sample above you can see @nowak is using updateStateUsing(). I’ve also been calling it action in all of my googling today till about an hour ago when I figured out the difference.
awcodes
awcodes4w ago
Also, only form input columns have the concept of updating state
karpadiem
karpadiem4w ago
I’m currently trying to also get ->updateStateUsing() to work on a form input column (i’ve tried checkbox, and toggle)
awcodes
awcodes4w ago
Normal columns are essentially readonly. Vs something like a textInputColumn which does interact with state.
karpadiem
karpadiem4w ago
This is my particular column. ->getStateUsing() works great, but can not get ->updateStateUsing() to work to save my life. CheckboxColumn should be one of the form input columns right?
/checkbox column to hold the task complete/uncomplete controls
Tables\Columns\CheckboxColumn::make('status')
->label('Completed')
->getStateUsing(fn ($record) => $record->status == 'completed')
->updateStateUsing(function ($state, Task $record){
//dd($record);
if ( $state == 1) {
$record->staus = 'completed';
$record->save();
} else {
$record->status = 'active';
$record->save();
}
return $state;
})
->sortable(),
/checkbox column to hold the task complete/uncomplete controls
Tables\Columns\CheckboxColumn::make('status')
->label('Completed')
->getStateUsing(fn ($record) => $record->status == 'completed')
->updateStateUsing(function ($state, Task $record){
//dd($record);
if ( $state == 1) {
$record->staus = 'completed';
$record->save();
} else {
$record->status = 'active';
$record->save();
}
return $state;
})
->sortable(),
awcodes
awcodes4w ago
Also, where are you getting updateStateUsing() ?
karpadiem
karpadiem4w ago
Not sure what you mean here. Are you asking where i am finding I should use it?
awcodes
awcodes4w ago
I’m asking why you are using that method when it is not part of the lifecycle for that component. It should be afterStateUpdated() Just feels like you are trying to augment the state out of order for the lifecycle
karpadiem
karpadiem4w ago
Because im struggling to figure out and find documentation that I can understand that explains how i would translate a checkbox being checked, to a column being updated with the value of “completed”. From digging around im seeing examples of form input columns using updateStateUsing() to get this done. I started digging into the code to figure it out and CheckboxColumn.php uses Concerns\CanUpdateState so i thought I was on the right track.
awcodes
awcodes4w ago
Keep in mind that form input columns are not the same thing as a form input.
karpadiem
karpadiem4w ago
I understand that. 🙂 This github issue in particular i was referencing. https://github.com/filamentphp/filament/issues/6662#issuecomment-1574574466
GitHub
updateStateUsing() makes table select go back to first value · Issu...
Package filament/filament Package Version v2.17.44 Laravel Version v10.13.0 Livewire Version No response PHP Version PHP 8.1.10 Problem description After changing the value for the select "Shi...
awcodes
awcodes4w ago
Column inputs are limited in what they can do compared to actual form input fields. Ah, that’s v2. That’s where it’s coming from. Are you on filament v2?
karpadiem
karpadiem4w ago
No, im on v3.
awcodes
awcodes4w ago
Ok, so that method isn’t the same on v3.
karpadiem
karpadiem4w ago
I thought that could be a risk of old info, but again, with the CheckboxColumn class using CanUpdateState i assumed that was correct. So, should my code look more like this:
Tables\Columns\CheckboxColumn::make('status')
->label('Completed')
->getStateUsing(fn ($record) => $record->status == 'completed')
->beforeStateUpdated(function ($record, $state) {
Log::info('state: '.$state);
if ( $state == 1) {
$record->status = 'completed';
$record->save();
} else {
$record->status = 'active';
$record->save();
}
})
->sortable()
Tables\Columns\CheckboxColumn::make('status')
->label('Completed')
->getStateUsing(fn ($record) => $record->status == 'completed')
->beforeStateUpdated(function ($record, $state) {
Log::info('state: '.$state);
if ( $state == 1) {
$record->status = 'completed';
$record->save();
} else {
$record->status = 'active';
$record->save();
}
})
->sortable()
awcodes
awcodes4w ago
Should probably be after state updated, but you still need to return the state. So in the callback, do what you need to with the state, but return what the modified state should be for the column.
karpadiem
karpadiem4w ago
Ok, that worked. I was confused by the documentation saying “ // Runs after the state is saved to the database.” as if it was already stored in the database.
awcodes
awcodes4w ago
Well it is after, but the stored state can be different than the displayed state. If that makes sense.
karpadiem
karpadiem4w ago
I do get that, but I think im still struggling with the wording. I have it working now so im happy. Thanks for your help and patience as i wrap my brain around it. 😄
Solution
LeandroFerreira
We cannot register an Action here, I guess. I created a header action as a workaround. Is it elegant? No, but it might work for your case. ListPage
protected function getHeaderActions(): array
{
return [
Actions\Action::make('createRoute')
->form([
TextInput::make('name')->required()->maxLength(255),
ColorPicker::make('color')->required(),
])
->action(function (array $arguments, array $data) {
$data['id'] = $arguments['id'];
Route::create($data);
})
->modalHeading('Create Route')
->modalSubmitActionLabel('Create')
->modalWidth('lg')
->extraAttributes(['class' => 'hidden'])
];
}
protected function getHeaderActions(): array
{
return [
Actions\Action::make('createRoute')
->form([
TextInput::make('name')->required()->maxLength(255),
ColorPicker::make('color')->required(),
])
->action(function (array $arguments, array $data) {
$data['id'] = $arguments['id'];
Route::create($data);
})
->modalHeading('Create Route')
->modalSubmitActionLabel('Create')
->modalWidth('lg')
->extraAttributes(['class' => 'hidden'])
];
}
TextInputColumn::make('route_id')
->updateStateUsing(function (Page $livewire, $state, Model $record) {
if ($state > 1 || $state !== null) {
//...
$livewire->mountAction('createRoute', ['id' => $state]);
}
})
TextInputColumn::make('route_id')
->updateStateUsing(function (Page $livewire, $state, Model $record) {
if ($state > 1 || $state !== null) {
//...
$livewire->mountAction('createRoute', ['id' => $state]);
}
})
nowak
nowak4w ago
Wow! thank you! It seems like this is the only way, and this solves the issue.
LeandroFerreira
ugly, but it should work ✌️
nowak
nowak4w ago
We can't have it all 🙂
LeandroFerreira
agree