Selfupdating Modal-Form
Hi - I migrate my old frontend to filament and stumbled on a problem. Given the following scenario: I have a resource that manages a Model (A) and I made an Edit-Action for model A. Now A has a BelongsTo-Relationship to another model (B) and a BelongsToMany-Relationship (with pivot-values) to yet another model (C) and C has a BelongsTo-Relationship to B. Model C is the interesting part here, since it stores templates for dynamic configurations of A (with "dynamic" I mean that it stores a config-key, a type like text-input or select and some template-data).
What I want to have is a Select which updates the BelongsTo-B-relationship (pretty easy) but that also updates the form to show all relevant C-entries and make them editable. This might sound a bit confusing, so I've added a small demonstration. Whats happening in there is that when "Kein Fulfiller" is set, the model_b_id in A is set to null, when it is set to "FF-2222" the model_b_id is set to 2 -> But here is the catch: It is NOT updated in the DB until "Speichern" has been clicked. In the background happens an ajax-request via JQuery that loads the C-entries which correspond to B and displays them. When the user is done with it's edits, the user clicks "Speichern" and then everything is stored. I have no idea where to begin this. So how can I achieve the behavior in the demonstration in filament?
Solution:Jump to solution
Okay - for anyone reading this: I can't share my code here directly, but I can write what I did in the end.
Within the
form()
-function I wrote a callback that loads all static form-components and added ->reactive()
to the Select-Component that controls model_b_id in my Model A-Entry (let's just call it "Joe" for fun π ). Still within the callback-function I've added all form-element-templates (Model C in my example) and stored them in a Model-B-ID-indexed array -> make sure you use the '.'-syntax here (like myGroupedItems.$myCustomTemplate->id.value
), since this can be used later to call Laravel's ->sync()
-function within the ->action()
-function to work like this: $modelA->modelCBelongsToManyRelationship()->sync($data['myGroupedItems'])
). The array is like this: $groups = ['1' => [*myItemsForModel-B-Entry-1], 2=> ...]
. Then (still within the form-callback) I grouped them together like this and added a visable function:
```
foreach ($groups as $key => $items){
$retval[] = Forms\Components\Group::make($items)...13 Replies
Refer this, you will get some idea.
https://filamentphp.com/docs/2.x/forms/advanced#dependant-fields--components
If you use the
Group
component to organize the form into sections, I think this can be done with a combination of ->visible()
to control groups based on the value of other fields before, and some dynamic ->options()
on the Select field.Getting creative with "visible" might do the trick... only problem is that I would have to load the entire model-C table into the form - plus some of these fields are required and I don't know if that will bite itself a bit with several invisible groups...
Those dependent fields sound like a good idea - especially in combination with @pboivin's input on using visible.
But tbh it looks a bit hacky to me... Ain't there another way to get only the fields I need and use an ajax-request (somehow) to update the whole form-schema itself?
You get AJAX pretty much for free in the context of Livewire... just figure out how to retrieve what you need from a Livewire property or method.
You're in a modal so there is added complexity but if you find a way to encapsulate your form in its own Livewire component, it would be relatively easy to make a dynamic form schema in
getFormSchema()
I think I can do it with livewire-events. Found a pretty good resource here: https://5balloons.info/refreshing-re-render-a-livewire-component
Okay - I think I might have to admin defeat here... I don't see how to do it. I thought I could just make a custom component and let it implement the HasForms-Interface, but I don't see how, without passing the initial values first.
The dependent fields seem to be a good starting point. By making the initial select being reactive I could come up with a custom field - but that doesn't "feel" right, since it would have to be a field consisting of multiple fields.
I think for now imma go with the group-approach that @pboivin suggested.
I lost track of your initial message... you said this is on your front-end, so it should simplify things when it comes to custom components (outside of the panel, already in "custom" land). What do you use to drive your modal?
a regular Filament-EditAction. π (hence my other question that mountUsing is not working as it should).
#Empty model on mountUsing that one
Ok, makes sense. Not sure if this can simplify things for you or not but, in some rare cases, I've chosen to implement a custom modal instead of using a table or page Action, specifically to get more flexibility in how I can wrap things in their own Livewire components.
hmm right now I have to focus on "getting stuff done no matter what" π
Could you take a look on the mountUsing-Problem aswell please?
Sure, getting there, I'm walking up the issue list π
Hahahaha okay - thank you. π
Solution
Okay - for anyone reading this: I can't share my code here directly, but I can write what I did in the end.
Within the
form()
-function I wrote a callback that loads all static form-components and added ->reactive()
to the Select-Component that controls model_b_id in my Model A-Entry (let's just call it "Joe" for fun π ). Still within the callback-function I've added all form-element-templates (Model C in my example) and stored them in a Model-B-ID-indexed array -> make sure you use the '.'-syntax here (like myGroupedItems.$myCustomTemplate->id.value
), since this can be used later to call Laravel's ->sync()
-function within the ->action()
-function to work like this: $modelA->modelCBelongsToManyRelationship()->sync($data['myGroupedItems'])
). The array is like this: $groups = ['1' => [*myItemsForModel-B-Entry-1], 2=> ...]
. Then (still within the form-callback) I grouped them together like this and added a visable function:
This makes the modal updates itself, whenever the value of Joe changes. Now to have the values beeing loaded when the modal opens, I've created a callback-function within ->mountUsing()
filling all the form-fields. And last-but-not-least added the data-storage (see above) and the whole thing was done.
I will mark this as a solution for now to give the thread an end, but tbh I am not really happy with this, since it would be cooler to have a callback on Joe himself that can alter the whole form. If someone finds a way to do this within the EditAction::make()
-spectrum of a resource, feel free to add (and ping me please). π
Warning: This thing seems to ignore ModelPolicies! Make sure to make it only availble to the appropiate users (->visible()
on the action itself helps)For anyone to find this - the problem is better explained here: #Selfupdating / On-The-Fly / Dynamic Modal Form on Resources (Tutorial)