How to determine if a form has changes

This has been posted elsewhere, but the solution hasn't been quite clear or evident. Making a new post to continue the discussion we started in order to not hijack the original question, which was slightly different. The isDirty() method, when applied to '$record' will always be false, as $record will be linked to the original model. When applied to an instance of a livewire component, it still seems to return false whether any field has changed or not. What I am trying to do is this: within an action that is in a form in a resource, to have different behavior based on if there any fields that have been changed or not by the user. I was able to do this by comparing the state to the original data, but this seems a bit cumbersome and also requires formatting datetimes and boolean values, since they maybe represented slightly differently in the database versus the livewire component.
Solution:
here's one option, in your EditRecord and CreateRecord page classes add: ```php public bool $isDirty = false; ...
Jump to solution
15 Replies
dynamictasks
dynamictasksOP4w ago
@toeknee thank you for helping with this. This is a new discussion so we can look at this more specifically based on our conversation. I created some sample code to show what I am trying to do:
return $form
->schema([
Forms\Components\Section::make('someActionsHere')
->schema([
Actions::make([
Actions\Action::make('doSomethingHere')
->action(function ($record) {
if ($record->isDirty()) { // this clearly won't work as it is referring to the original model
// do something here
Notification::make('changes') // just an example
->title('You have unsaved changes.')
->send();
} else {
// do something else here
Notification::make('noChanges') // just an example
->title('All changes have been saved.')
->send();
}
})
]),
]),
...
return $form
->schema([
Forms\Components\Section::make('someActionsHere')
->schema([
Actions::make([
Actions\Action::make('doSomethingHere')
->action(function ($record) {
if ($record->isDirty()) { // this clearly won't work as it is referring to the original model
// do something here
Notification::make('changes') // just an example
->title('You have unsaved changes.')
->send();
} else {
// do something else here
Notification::make('noChanges') // just an example
->title('All changes have been saved.')
->send();
}
})
]),
]),
...
@toeknee you also mentioned using the wire dirty method, but I am not sure where I would put this since I don't have a custom blade view or anything like that for this resource.
awcodes
awcodes4w ago
You can inject $livewire into the ->action(function($livewire)) callback and access it there.
dynamictasks
dynamictasksOP4w ago
Thanks. I had tried that before, and that's how I was comparing the $livewire data vs the $record data to see if there were any changes. This worked, but it just felt like I was doing too much to get it done. I was hoping there was something like an 'isDirty()' or 'hasChanges()', but those don't seem available in this context:
public static function form(Form $form): Form
{
return $form
->schema([
Forms\Components\Section::make('someActionsHere')
->schema([
Actions::make([
Actions\Action::make('doSomethingHere')
->action(function ($livewire) {
if ($livewire->isDirty()) { // Method 'isDirty' not found in \Livewire\Component
// do something here
Notification::make('changes') // just an example
->title('You have unsaved changes.')
->send();
} else {
// do something else here
Notification::make('noChanges') // just an example
->title('All changes have been saved.')
->send();
}
})
]),
]),
...
public static function form(Form $form): Form
{
return $form
->schema([
Forms\Components\Section::make('someActionsHere')
->schema([
Actions::make([
Actions\Action::make('doSomethingHere')
->action(function ($livewire) {
if ($livewire->isDirty()) { // Method 'isDirty' not found in \Livewire\Component
// do something here
Notification::make('changes') // just an example
->title('You have unsaved changes.')
->send();
} else {
// do something else here
Notification::make('noChanges') // just an example
->title('All changes have been saved.')
->send();
}
})
]),
]),
...
How would I determine if the component has any changes in this case? Thanks in advance. This is what I had before that was working, but just doesn't seem very good:
->action(function ($livewire, $record) {
$original = collect($record->getAttributes());
$original['created_at'] = Carbon::parse($original['created_at'])->format('Y-m-d H:i:s');
$original['updated_at'] = Carbon::parse($original['updated_at'])->format('Y-m-d H:i:s');

$current = collect($livewire->data);
$current['created_at'] = Carbon::parse($current['created_at'])->format('Y-m-d H:i:s');
$current['updated_at'] = Carbon::parse($current['updated_at'])->format('Y-m-d H:i:s');

if ($original != $current) { // strict comparison was not working either
// do something here
} else {
// do something else here
}
})
->action(function ($livewire, $record) {
$original = collect($record->getAttributes());
$original['created_at'] = Carbon::parse($original['created_at'])->format('Y-m-d H:i:s');
$original['updated_at'] = Carbon::parse($original['updated_at'])->format('Y-m-d H:i:s');

$current = collect($livewire->data);
$current['created_at'] = Carbon::parse($current['created_at'])->format('Y-m-d H:i:s');
$current['updated_at'] = Carbon::parse($current['updated_at'])->format('Y-m-d H:i:s');

if ($original != $current) { // strict comparison was not working either
// do something here
} else {
// do something else here
}
})
toeknee
toeknee4w ago
I can't actually see any way to check programatically if anything is different. I thought we had isDirty() on the form within the livewire component but we don't. I think we could only do a collection compare, but then we would need to assoc it to make sure custom virtual / dehydrated fields are ignored too.
dynamictasks
dynamictasksOP4w ago
Thanks for checking. I thought maybe I was missing something. I had seen some other conversations and one suggestion was to update a property using afterUpdate() and check that property, but I think that would be too much to do for every field. But, might be the best option at this time.
awcodes
awcodes4w ago
Livewire does track it but I think it might only be on the JS side and therefore not available php side. Looking at filament’s own unsaved changes alert it’s all JS side.
Solution
awcodes
awcodes4w ago
here's one option, in your EditRecord and CreateRecord page classes add:
public bool $isDirty = false;

public function updating() {
$this->isDirty = true;
}
public bool $isDirty = false;

public function updating() {
$this->isDirty = true;
}
Then you should be able to check it in the action callback:
->action(function ($livewire) {
dd($livewire->isDirty);
})
->action(function ($livewire) {
dd($livewire->isDirty);
})
dynamictasks
dynamictasksOP3w ago
Thanks @awcodes This works perfectly! Thanks to you and @toeknee for helping with this.
toeknee
toeknee3w ago
Great work @awcodes I forgot all about the updating method.
awcodes
awcodes3w ago
It’s kind of a hack and there may be some caveats but glad it works for you.
dynamictasks
dynamictasksOP3w ago
Definitely a great solution for what I need. Thanks again!
awcodes
awcodes3w ago
Could throw it in a trait too, if you need it on more than one resource.
dynamictasks
dynamictasksOP3w ago
Nice, I will likely need it in more than one resource.
awcodes
awcodes3w ago
Haven’t tested further, but just fyi, I think the reason $record->isDirty() is always false is because something, possibly filament, is setting the record property to Locked. Possibly something to do with partial hydration. It’s above my head at that point. 😂 😅
dynamictasks
dynamictasksOP3w ago
lol. Then in that case it is way, way above my head. But, I was expecting initially that $record->isDirty() would work, but quickly found out otherwise. 😂

Did you find this page helpful?