F
Filament2mo ago
slamx_

How to call function inside custom form field

I'm new in writing a custom form field. I watched the laracast from Dan but I want to trigger some custom functions inside the Field Class. I tried a regular wire:click with naming the method but I get the following response: Unable to call component method. Public method [$example] not found on component This is my example form field: namespace App\Forms\Components; use Filament\Forms\Components\Field; class CloudFileSelect extends Field { protected string $view = 'forms.components.cloud-file-select'; public function example() { dd('example'); } } And this is the view: <x-dynamic-component :component="$getFieldWrapperView()" :field="$field" > <div class="z-10" x-data="{ state: $wire.$entangle('{{ $getStatePath() }}') }"> <button class="button hover:cursor-pointer" type="button" wire:click="example()">Open Example</button> </div> </x-dynamic-component> I would be happy if someone can help me or give me a good source with a comprehensive example for writing my own field.
45 Replies
krekas
krekas2mo ago
what's the purpose of this button? Don't think you can have button in a form like that
Dennis Koch
Dennis Koch2mo ago
Form Fields aren't Livewire components. You can't use wire:click here. You would need to define example() method on the Page itself
slamx_
slamx_2mo ago
ok - thanks for the feedback - How about event listeners. How I can implement a alpine listener to a custom form field?
Dennis Koch
Dennis Koch2mo ago
The same as you would define any Alpine listener?
slamx_
slamx_2mo ago
ok.. like I said its my first custom form field and I dont find that much inside the docs or web
awcodes
awcodes2mo ago
You can also register actions and listeners in the setUp() method of the field.
krekas
krekas2mo ago
what this button would do? maybe there are better alternatives like hint action
slamx_
slamx_2mo ago
Do you have a code example for me (link)? Where I also can found other methods that are possible? 🙂
awcodes
awcodes2mo ago
Have a look at the Builder and Repeater components in core. You can register the action in setup then use the action directly in the view.
slamx_
slamx_2mo ago
with extraItemActions?
awcodes
awcodes2mo ago
Tiptap editor plugin and Curator plugin use this approach too. No, in the setup method ->registerActions() Those are just examples of how you can do it. Don’t try to copy them exactly. 🙂
slamx_
slamx_2mo ago
I'll see how far I get with the tips. Thank you 🙂 @awcodes short question, when I add a listner like that: @script <script> $wire.on('post-created', () => { // }); </script> @endscript and I dispatch for example post-created, the dont react inside the custom form field
Dennis Koch
Dennis Koch2mo ago
Where/how do you dispatch that event?
slamx_
slamx_2mo ago
inside a livewire component
Dennis Koch
Dennis Koch2mo ago
Can you share some code?
slamx_
slamx_2mo ago
sure: $this->dispatch('cl-selected', $data) @script <script> $wire.on('cl-selected', (data) => { console.log('triggered'); console.log(data); }); </script> @endscript
Dennis Koch
Dennis Koch2mo ago
This a bit low context. Can you share more of the component? Also please use code formatting.
slamx_
slamx_2mo ago
Thats the custom form field
<x-dynamic-component
:component="$getFieldWrapperView()"
:field="$field"
>
<div
class="z-10"
x-data="{
state: $wire.$entangle('{{ $getStatePath() }}'),
showModal: false
}"
x-init="
console.log('init cloud select');
">



<button class="button hover:cursor-pointer" type="button" wire:click="$dispatch('user-file-selector-show')">Cloud Datei auswählen</button>

</div>

@script
<script>
$wire.on('cl-selected', (data) => {
console.log('triggered');
console.log(data);
});
</script>
@endscript
</x-dynamic-component>
<x-dynamic-component
:component="$getFieldWrapperView()"
:field="$field"
>
<div
class="z-10"
x-data="{
state: $wire.$entangle('{{ $getStatePath() }}'),
showModal: false
}"
x-init="
console.log('init cloud select');
">



<button class="button hover:cursor-pointer" type="button" wire:click="$dispatch('user-file-selector-show')">Cloud Datei auswählen</button>

</div>

@script
<script>
$wire.on('cl-selected', (data) => {
console.log('triggered');
console.log(data);
});
</script>
@endscript
</x-dynamic-component>
The dispatch is from a livewirecomponent outside the form
krekas
krekas2mo ago
can you answer what in the form this button will do? i assume it's somewhere in the middle of the form? also, instead of one backtick for code highlight use three backtick and a language for syntax highlight
slamx_
slamx_2mo ago
the button open a livewire modal component that shows a file list
krekas
krekas2mo ago
but where it is placed? for me it seems a basic action button could be used instead
slamx_
slamx_2mo ago
Yes, it can also be an action, but for now it is more important to understand why the event listener does not work
awcodes
awcodes2mo ago
how did you register the listener? can you show that code? normally when you register a listern for a field you also provide the callback function there, not in the blade view. also this real should be an action. you are way over complicating it.
slamx_
slamx_2mo ago
do you mean as a listener inside the setUp?
awcodes
awcodes2mo ago
yes. since field's aren't livewire components you have to do it differently
slamx_
slamx_2mo ago
But they also listn to regular dispatch events?
awcodes
awcodes2mo ago
well, based on your code, the event you are dispatching doesn't have the same name as the event you are listening for. based on my experience, the stack approach does work well in this case, since it's always getting re-rendered everytime a livewire call is made.
krekas
krekas2mo ago
Action::make('name')
->dispatch('event-name');
Action::make('name')
->dispatch('event-name');
slamx_
slamx_2mo ago
I tryed
protected function setUp(): void
{
parent::setUp();

$this->registerListeners([
'cl-selected' => [
function (Component $component): void {

},
],
]);
}
protected function setUp(): void
{
parent::setUp();

$this->registerListeners([
'cl-selected' => [
function (Component $component): void {

},
],
]);
}
Perhaps I will briefly explain what exactly is to be communicated via the event. There is a file manager, or rather a file selection as a Livewire component, which returns the selected file as an event. Now the files should be listed in a filament form when you select a file for the area. The Button opens the Modal Livewire component
awcodes
awcodes2mo ago
ok. so that's a form event, $wire.dispatchFormEvent('cl-selected', '{{ $getStatePath() }}') an action will make this a thousand times easier
slamx_
slamx_2mo ago
But the modal selection is not part of the form - so a bit confused now :S ok, so when I use the action for the custom component, how I can add an Item to the state, or work with the dispatch() data?
awcodes
awcodes2mo ago
you don't have to dispatch anything
slamx_
slamx_2mo ago
is there anything outside the docs maybe where I can take a look on? ok sure - the dispatch comes form the livewire component. But how I listen for that inside the form component? I removed the button and add a Action instead.
awcodes
awcodes2mo ago
protected function setUp(): void
{
parent::setUp();

$this->registerActions([
fn (): Action => Action::make('fileSelect')
->form([...])
->action(function(Field $component, array $data) {
$state = $component->getState();

$state['file_field'] = $data['file_selected'];

$component->state($state);
})
}
protected function setUp(): void
{
parent::setUp();

$this->registerActions([
fn (): Action => Action::make('fileSelect')
->form([...])
->action(function(Field $component, array $data) {
$state = $component->getState();

$state['file_field'] = $data['file_selected'];

$component->state($state);
})
}
in the view.blade.php
{{ $getAction('fileSelect') }}
{{ $getAction('fileSelect') }}
slamx_
slamx_2mo ago
thats fine.. but now how to communicate between the livewire component thats outside of the form with the form component
krekas
krekas2mo ago
form is on create or edit page so you should listen on those pages. where are you listening for the events?
awcodes
awcodes2mo ago
you can use arguments in the action to pass the data around.
slamx_
slamx_2mo ago
ok ok I think I may have expressed myself incorrectly. I have integrated a listener in the custom form field. The file selection is outside the form, which is why I work with the listener. But the listener not react after I dispatch the event inside the regular livewire component that shows up. Thats the listener I've add inside the custom form component.
@script
<script>
$wire.on('cl-selected', (data) => {
console.log('triggered');
console.log(data);
});
</script>
@endscript
@script
<script>
$wire.on('cl-selected', (data) => {
console.log('triggered');
console.log(data);
});
</script>
@endscript
awcodes
awcodes2mo ago
like i said earlier, using stacks like that is probably not going to work in this use case, because it won't push to the stack during rerender.
slamx_
slamx_2mo ago
but inside the setUp() should it work?
awcodes
awcodes2mo ago
yes, but it's a form event, not a native livewire event doing it that way
slamx_
slamx_2mo ago
Does this mean that regular native events cannot be used in form fields?
awcodes
awcodes2mo ago
$this->registerListeners([
'cl-selected' => [
function (Field $component, string $statePath, array $arguments
) { do something with $arguments },
],
]);
$this->registerListeners([
'cl-selected' => [
function (Field $component, string $statePath, array $arguments
) { do something with $arguments },
],
]);
x-on:click="$wire.dispatchFormEvent('cl-selected', {{ $getStatePath() }}, arguments"
x-on:click="$wire.dispatchFormEvent('cl-selected', {{ $getStatePath() }}, arguments"
slamx_
slamx_2mo ago
but your dispatch is inside the form field, the dispatch I mean comes from a regular livewire component As an example, I have the Livewire component in which the form is also and another one that represents a modal with a file listing. The event comes from the file listing component.
awcodes
awcodes2mo ago
i'd have to see the code to the whole thing to help further. I still think you're over complicating the whole thing.