How to create custom actions and show them in custom views?
Hello, I've created a custom component for a custom view page where I want to show all the help tickets related to the current booking. I've successfully shown the records needed but I would also like to display a button in this component where it's possible to create a new ticket for that booking. I thought to do it using actions, but I can't understand how to build a custom action and how I can show it in a custom view. Thank you very much in advance for your help
72 Replies
#drop-in-action
Assuming you’re in a form builder.
No, I'm in the admin panel and I'm creating a custom ViewResource page. In this custome ViewResource page I have created a custom component where inside I successfully rendered a table component. Now, in the same component, I would like to show a custom button using action where I could show a modal to create a new ticket related to the current booking but I can't understand how to render and how to create a custom action.
Drop in action should still work for you.
Not sure if it works in view resource but there is also suffix and prefix actions that can be used on specific fields.
Every Action class has methods on it to use modals and forms and ways to interact with the ->action() method to process the data.
I have followed the instructions on the plugin and tried to put the DropInAction inside the getActions() method of the viewBooking class but nothing Is rendered and no error in console. How should I render the component in the view?
It doesn’t go in the getActions. It’s used as a field in the form schema.
If you’re wanting to just have an action in getActions then you don’t need a plugin. You can use regular Actions there.
Oh ok, now i got your point about the drop in action! I will try it in the form schema.
And just register it in your ViewResource getActions method.
Come down to where you’re trying to register the action. If you want it in the top right it’s a normal action inside getActions. If you want to have it inside a section or panel then you’ll need drop in action.
I need it in a panel. So I can simply use it as form component and render it with $this->form using drop in action, from what I understand..
Honestly, still not 100% sure drop in works inside the view context. But in my head it should.
Theoretically yes
You just add it to the form schema as if it was a form input.
And then, I can call on that action() all the methods I can find in the docs to interact with it...
Yes.
It will be included in v3 out of the box.
Thanks, I'll keep you posted on this!
That would be wonderful!
No worries. Let me know. If it doesn’t work I can modify to work in the view context, maybe.
Thanks a million! I'll post an update soon!
Just a weird situation to edit something from a view context. Seems counter intuitive but I can see a use case for it.
Yes, it may see. But I have the right use case for it 😁
UPDATE: Drop in Action correctly renders the button on the component, but it seems it cannot open a modal. Even if call the form method() on the action within the needed fields and than call the requiresConfirmation() method on it. The loading spinner starts and suddenly stops and nothing happens. Is there anything I can do to make the pop up open?
I thought I just saw a post from you, did you solve it?
No, sorry I wasn't able to find this one anymore and created a new one. Now I found this again and prefer to stick on this one without creating many posts.
Here's the code that I'm using :
protected function getFormSchema(): array
{
return [
DropInAction::make('test')
->disableLabel()
->execute(function (Closure $get, Closure $set) {
return Action::make('create_ticket')
->icon('heroicon-o-plus')
->label('Nuovo ticket')
->action(fn () => null)->form([
TextInput::make("prova")->required()
])->requiresConfirmation();
}),
];
}
Ok pleae use the #❓┊help or the #drop-in-action as it gets too crowed in here
How to use it?
How to use what?
Got it now
The tags. Sorry I'm not expert with discord. Just got how to use it
No problem
#❓┊help #drop-in-action
two seconds I'll have a working example for you
Thanks a million
Is a fully working example in our $form->schema[]);
ensure your Action is an uses:
use Filament\Forms\Components\Actions\Action;
It seems to doesn't work for me😭
This is my full component :
<?php
namespace App\Http\Livewire\Booking;
use Closure;
use Filament\Forms;
use Livewire\Component;
use App\Models\PointOfSale;
use Filament\Facades\Filament;
use Illuminate\Support\Facades\Http;
use Filament\Forms\Components\TextInput;
use Http\Client\Exception\RequestException;
use Filament\Forms\Components\Actions\Action;
use Awcodes\DropInAction\Forms\Components\DropInAction;
class CreateTicketAction extends Component implements Forms\Contracts\HasForms
{
use Forms\Concerns\InteractsWithForms;
public $record;
public function mount($record){
$this->record = $record;
$this->address = "Via della Scrofa, 22, Roma";
}
protected function getFormSchema(): array
{
return [
DropInAction::make('test')
->disableLabel()
->execute(function (Closure $get, Closure $set) {
return Action::make('create_ticket')
->icon('heroicon-o-plus')
->label('Nuovo ticket')
->form([
TextInput::make('prova')->required(),
])
->requiresConfirmation()
->action(function () {
});
}),
];
}
public function render()
{
return view('livewire.booking.create-ticket-action');
}
}
If you inspect the browser, what does the console say?
Absolutely nothing
What version of Filament?
2.0
Please upgrade filament 2.0 is very old
What's the latest version? The documentation says : "composer require filament/filament:"^2.0""
that's greater or equal than 2.0
In my composer.json and composer.lock I've modified from "filament/filament" : ""^2.0"" to "filament/filament": " * ", but the is still not showing. Any suggestions?
You said you were on exactly 2.0
oh sorry, so it was correct, my bad
you should be on like 2.17.17
So i reverted back to the original version i had.
As I said it may not work in the view context. Not sure without looking at the core code, but the view context may not have the necessary modal renderer to allow this to work.
Ahh I missed this, it was working with the code I provided in my page form schema
Got the point. I was surprised because @toeknee_iom said it was working for him.
Is there a way i can create a modal renderer?
Not that world work this way out of the box. I’ll do some digging and get back to you later today.
you can use a normal method for rendering modals in livewire
Give me a few hours. Just waking up here.
Take your time. Thanks a million!
What do you mean?
rough example:
https://tonylea.com/creating-modals-in-laravel-livewire
But I'd wait for awcodes to see if he can do it for dropinaction first
Tony Lea - Developer and Designer from SoCal
Creating Modals in Laravel Livewire
In this tutorial I will show you how to create simple modals in Laravel Livewire. We are going to make use of an awesome package called LivewireUI Modal. This package allows you to create some re-usab...
I will wait for him. Thanks for the moment
@gianmarcovarrone Just tired this with a one of my app and it's working fine. Make sure you are using the right Action.
use Filament\Forms\Components\Actions\Action;
Is your custom view page extending Filament's ViewRecord class?I think that I'm already using it..
right, that's just the component. what class are you using to render the whole page
This the viewResource page that I'm using to render the custom view
<?php
namespace App\Filament\Resources\BookingResource\Pages;
use Filament\Tables\Table;
use Filament\Pages\Actions;
use Filament\Tables\Contracts\HasTable;
use App\Filament\Resources\BookingResource;
use Filament\Tables\Concerns\InteractsWithTable;
class ViewBooking extends ViewRecord implements HasTable
{
use InteractsWithTable;
protected static string $resource = BookingResource::class;
protected static string $view = 'filament.resources.booking-resource.pages.view-booking';
protected function getActions(): array
{
return [
//
];
}
}
as a test, can you add ->modalHeading('test') to the action. i'm think it just doesn't know it needs to be a modal.
everything looks ok, and it's work on a ViewRecord page for me.
Ok. something else is going on there. your code works in my page.
probably because it's a custom livewire component so it's not emitting up to the parent which has the listener for executing the Action.
I think I'm missing something. Should I register this action in the getActions() of the view page?
What type of event should I emit from the livewire component?
I think the problem is that ViewRecord, just like Create and Edit is a form, and your component is also rendering a form which you can't do in html.
do you even need a custom component?
What do you mean? What I'm trying to build is a component where the user can see all the help tickets related to the current booking, create a new one or post updates on existing tickets..
I just use the DropInAction in my form schema on the Resource, because it's also needed on Create and Edit
Wouldn't that just be a Relation Manager?
I think from his screenshot, he want's to actually build a modal in a form in any place and not inside another form like you use DropInAction for
yea, it's clicking now.
so actions don't work natively inside the main form on the page, that's why DropInAction was created. So to do it in a custom component, i'm thinking you'd have to implement your own modal and handle all the logic yourself.
but it seems like to me this would just be a standard Relation Manager on a regular view record page.
Yes, the issue is that no modal is shown. If I pass the data array to the actions() the action is executed correctly. But, if I use requireConfirmation() no modal is shown and no action is executed.
yea, because your component is a child livewire component. and the code that handles rendering modals is on the parent/page livewire component. so it never receives the event to show the modal.
Yes, the relation manager would work. I should just place it in a component to fit in the screenshot above. Here's because I decided to go with custom component...
So, the code that handles the modal is here protected static string $view = 'filament.resources.booking-resource.pages.view-booking'; right?
If that's where you're overriding the ViewRecord view to then yea.
Well, not exactly there, sorry. I meant in the Class. Yes.
So, I can avoid building a livewire component and simply use the code inside the main class ViewBooking to call the Drop In Action
ViewBooking, CreateBooking, EditBooking all use the same Page class, but you could just define the form schema on ViewBooking if you need it to be different than Create or Edit.
When you define form schema on the BookingResource it's just used as a way to keep from repeating it all on the various record classes.
I've updated my code. I deleted the child livewire component and tried to move all the code to the parent view. I moved my code for drop in action inside the getFromSchema() on the ViewBooking class as follow : ViewBooking Class <?php
namespace App\Filament\Resources\BookingResource\Pages;
use Closure;
use Filament\Tables\Table;
use Filament\Pages\Actions;
use Filament\Forms\Contracts\HasForms;
use Filament\Tables\Contracts\HasTable;
use Filament\Forms\Components\TextInput;
use Filament\Resources\Pages\ViewRecord;
use App\Filament\Resources\BookingResource;
use Filament\Forms\Components\Actions\Action;
use Filament\Forms\Concerns\InteractsWithForms;
use Filament\Tables\Concerns\InteractsWithTable;
use Awcodes\DropInAction\Forms\Components\DropInAction;
class ViewBooking extends ViewRecord implements HasTable
{
use InteractsWithTable, InteractsWithForms;
protected static string $resource = BookingResource::class;
protected static string $view = 'filament.resources.booking-resource.pages.view-booking';
protected function getActions(): array
{
return [
//
];
}
protected function getFormSchema(): array
{
return [
DropInAction::make('test')
->disableLabel()
->execute(function (Closure $get, Closure $set) {
return Action::make('create_ticket')
->icon('heroicon-o-plus')
->label('Nuovo ticket')
->form([
TextInput::make('prova')->required(),
])
->requiresConfirmation()
->action(function () {
null;
});
}),
];
}
}
This is how I'm rendering it in the blade view : <div>
<div class="p-2 space-y-2 bg-white rounded-xl shadow dark:border-gray-600 dark:bg-gray-800">
<div>
<div class="flex items-center justify-between gap-8 py-3">
<h2 class="text-lg font-semibold tracking-tight filament-card-heading">
Tickets
</h2>
{{$this->form}}
</div>
<div aria-hidden="true" class="filament-hr border-t dark:border-gray-700"></div>
<div class="pt-2">
@livewire("booking.tickets", ["record"=>$record])
</div>
</div>
</div>
</div>
But I keep having the same issue as the video above 😅is your blade view wrapped with
<x-filament::page></x-filament::page>
?
also, trying my best here. i don't really have anything setup to test / troubleshoot with. sorry.No, It wasn't wrapped. I did wrap it and got the following error: Method Awcodes\DropInAction\Forms\Components\DropInAction::livewire does not exist.
Ok the error disappeared. This is the view code :
<x-filament::page>
<div>
<div class="flex">
<h1 class="filament-header-heading text-2xl font-bold tracking-tight mr-3">
{{$record->name}}
</h1>
<div>
<span class="fi fi-{{$record->locale == "en" ? "gb" : $record->locale}} h-8 w-8 rounded-full" ></span>
</div>
</div>
<div class="mt-4 lg:flex">
<div class="flex mr-3">
<x-heroicon-o-phone class="h5 w-5 shrink-0 mr-2"/>
<a href="tel:{{$record->phone_number}}" class="text-primary-600">{{$record->phone_number}}</a>
</div>
<div class="flex mr-3">
<x-heroicon-o-mail class="h5 w-5 shrink-0 mr-2"/>
<a href="mail:{{$record->email}}" class="text-primary-600">{{$record->email}}</a>
</div>
</div>
<div class="lg:grid grid-cols-3 mt-5 gap-5">
<div>
<div class="p-2 space-y-2 bg-white rounded-xl flex items-center justify-center shadow dark:border-gray-600 dark:bg-gray-800">`
` <div class="rounded-full border p-2">
<x-heroicon-o-download class="h-7"/>
</div>
<span style="margin-top:0;" class="p-0 pl-3 font-medium">
Scarica contratto
</span>
</div>
<div class="p-2 space-y-2 bg-white rounded-xl mt-3 shadow dark:border-gray-600 dark:bg-gray-800">
@livewire("booking.details", ["record"=>$record])
</div>
<div class="p-2 space-y-2 bg-white rounded-xl mt-3 shadow dark:border-gray-600 dark:bg-gray-800">
@livewire("booking.payments", ["record"=>$record])
</div>
</div>
<div>
<div class="p-2 space-y-2 bg-white rounded-xl shadow dark:border-gray-600 dark:bg-gray-800">
@livewire("booking.history", ["record"=>$record])
</div>
</div>
<div>
<div class="p-2 space-y-2 bg-white rounded-xl shadow dark:border-gray-600 dark:bg-gray-800">
<div>
<div class="flex items-center justify-between gap-8 py-3">
<h2 class="text-lg font-semibold tracking-tight filament-card-heading">
Tickets
</h2>
{{$this->form}}
</div>
<div aria-hidden="true" class="filament-hr border-t dark:border-gray-700"></div>
<div class="pt-2">
@livewire("booking.tickets", ["record"=>$record])
</div>
</div>
</div>
</div>
</div>
</div>
</x-filament::page>