F
Filament7mo 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
36 Replies
karpadiem
karpadiem7mo ago
Im also struggling to get this working for a checkboxColumn. Were you able to figure it out @nowak ?
nowak
nowakOP7mo ago
Not yet, no :/
karpadiem
karpadiem7mo ago
I even added a log::info to the function and it looks like it doesn’t get hit at all.
LeandroFerreira
LeandroFerreira7mo ago
Hum.. maybe registering a custom action in a widget and dispatching with arguments 🤔
awcodes
awcodes7mo 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
karpadiem7mo ago
From what I’ve been reading, it seems like ->updateStateUsing() should be the way to modify how the state is stored right?
awcodes
awcodes7mo ago
Yes, but actions and state are 2 very different things.
karpadiem
karpadiem7mo 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
awcodes7mo ago
Also, only form input columns have the concept of updating state
karpadiem
karpadiem7mo ago
I’m currently trying to also get ->updateStateUsing() to work on a form input column (i’ve tried checkbox, and toggle)
awcodes
awcodes7mo ago
Normal columns are essentially readonly. Vs something like a textInputColumn which does interact with state.
karpadiem
karpadiem7mo 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
awcodes7mo ago
Also, where are you getting updateStateUsing() ?
karpadiem
karpadiem7mo ago
Not sure what you mean here. Are you asking where i am finding I should use it?
awcodes
awcodes7mo 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
karpadiem7mo 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
awcodes7mo ago
Keep in mind that form input columns are not the same thing as a form input.
karpadiem
karpadiem7mo 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
awcodes7mo 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
karpadiem7mo ago
No, im on v3.
awcodes
awcodes7mo ago
Ok, so that method isn’t the same on v3.
karpadiem
karpadiem7mo 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
awcodes7mo 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
karpadiem7mo 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
awcodes7mo ago
Well it is after, but the stored state can be different than the displayed state. If that makes sense.
karpadiem
karpadiem7mo 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
LeandroFerreira7mo ago
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
nowakOP7mo ago
Wow! thank you! It seems like this is the only way, and this solves the issue.
LeandroFerreira
LeandroFerreira7mo ago
ugly, but it should work ✌️
nowak
nowakOP7mo ago
We can't have it all 🙂
LeandroFerreira
LeandroFerreira7mo ago
agree
Willy
Willy5mo ago
I did this in a form. But how do I return the form state if the user cancels the action? For example, I have a toggle set to false, the user clicks it, changes it to true and displays the confirmation modal. If he cancels, my toggle persists as true.
Dennis Koch
Dennis Koch5mo ago
In this case you probably need to revert the state to again on cancel
Willy
Willy5mo ago
Could you give me an example? I tried a few ways, like changing the modalCancelAction but without success.
Dennis Koch
Dennis Koch5mo ago
That's what I would have tried. Probably you can register your own action though.
Want results from more Discord servers?
Add your server