F
Filamentβ€’2y ago
Husky110

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:
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)...
Jump to solution
13 Replies
Iliyas M
Iliyas Mβ€’2y ago
Filament
Advanced - Form Builder - Filament
The elegant TALL stack form builder for Laravel artisans.
Patrick Boivin
Patrick Boivinβ€’2y ago
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.
Husky110
Husky110OPβ€’2y ago
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?
Patrick Boivin
Patrick Boivinβ€’2y ago
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()
Husky110
Husky110OPβ€’2y ago
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.
Patrick Boivin
Patrick Boivinβ€’2y ago
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?
Husky110
Husky110OPβ€’2y ago
a regular Filament-EditAction. πŸ™‚ (hence my other question that mountUsing is not working as it should). #Empty model on mountUsing that one
Patrick Boivin
Patrick Boivinβ€’2y ago
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.
Husky110
Husky110OPβ€’2y ago
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?
Patrick Boivin
Patrick Boivinβ€’2y ago
Sure, getting there, I'm walking up the issue list πŸ˜‚
Husky110
Husky110OPβ€’2y ago
Hahahaha okay - thank you. πŸ™‚
Solution
Husky110
Husky110β€’2y ago
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)
->visible(fn(callable $get) => $get('model_b_id') == $key);
}
foreach ($groups as $key => $items){
$retval[] = Forms\Components\Group::make($items)
->visible(fn(callable $get) => $get('model_b_id') == $key);
}
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)
Husky110
Husky110OPβ€’2y ago
For anyone to find this - the problem is better explained here: #Selfupdating / On-The-Fly / Dynamic Modal Form on Resources (Tutorial)

Did you find this page helpful?