Creating Dynamic Modals in FilamentPHP with Livewire Action Arguments
When developing the storefront for Mondula, an IT asset management solution (https://filamentapps.dev/products/mondula-it-asset-management-system), I encountered an interesting challenge. One of the key features we wanted to implement was the ability for customers to place bids on products. The goal was to create a seamless user experience where clicking a "Place a Bid" button would open a modal pop-up, allowing the customer to enter their bid amount. Additionally, we needed to validate the bid amount against a minimum threshold.
In this blog post, I will walk you through how to set up this functionality, explaining each part of the code step-by-step and how you can pass action arguments to a Livewire component in FilamentPHP.
1. Blade Component for Triggering the Action
First, we need to trigger the bidding action programmatically. In the Blade component, we use Livewire's wire:click directive to call a function that loads the product:
<button class='px-4 py-2 bg-blue-600 text-white rounded-md capitalize inline-flex items-center gap-x-3' wire:click="loadProduct({{$product->id}})" > <svg><!-- SVG code for the icon --></svg> <span>Make a bid</span> </button>
This button, when clicked, will call the loadProduct method in our Livewire component, passing the product's ID as an argument.
2. Livewire Component: Index Class
Now, let's dive into the Livewire component that handles the bidding logic:
<?php namespace App\Livewire\Shop; use Filament\Forms; use Livewire\Component; use App\Models\Shop\Product; use App\Models\Shop\Bidding; use Illuminate\Contracts\View\View; use Filament\Forms\Contracts\HasForms; use Filament\Forms\Concerns\InteractsWithForms; use Filament\Notifications\Notification; use Filament\Actions\Contracts\HasActions; use Filament\Actions\Action; use Filament\Actions\Concerns\InteractsWithActions; class Index extends Component implements HasForms, HasActions { use InteractsWithForms; use InteractsWithActions; public ?array $data = []; public ?Product $product = null; // ... (other methods) }
This component uses the InteractsWithForms and InteractsWithActions traits, which provide the necessary functionality for working with Filament Forms and Actions.
3. The placeBidding Method
The heart of our bidding system lies in the placeBidding method:
public function placeBidding(): Action { return Action::make('placeBidding') ->requiresConfirmation() ->modalIcon('heroicon-o-banknotes') ->modalHeading('Place a Bid for ' . ($this->product->name ?? 'Product')) ->modalDescription('Once selected, you will get an email notification on how to proceed with payment') ->modalSubmitActionLabel('Place Bid') ->form([ Forms\Components\TextInput::make('amount') ->label('Bid Amount') ->prefix(Currency::getCurrency(setting('site.currency'))->symbol) ->numeric() ->required() ->minValue($this->product->minimum_bid_amount ?? '0') ->maxValue(10000) ->hint('Minimum Bid Amount: ' . ($this->product->minimum_bid_amount ?? '0')) ]) ->action(function (array $data, array $arguments){ $product = Product::find($arguments['product_id']); if ($product) { Bidding::updateOrCreate( [ 'shop_customer_id' => auth()->id(), 'shop_product_id' => $product->id, ], [ 'bid_amount' => (int)$data['amount'] ] ); Notification::make() ->title('Bid Placed Successfully') ->success() ->body('Your bid has successfully been sent') ->send(); } }); }
This method does several important things:
- It creates a Filament Action for placing a bid.
- It sets up a confirmation modal with a custom icon, heading, and description.
- It defines a form within the modal, including a text input for the bid amount.
- It sets validation rules for the bid amount, including a minimum value based on the product's minimum bid amount.
- It defines the action to be taken when the form is submitted, which includes creating or updating a Bidding record and sending a success notification.
4. The loadProduct Method
The loadProduct method is called when the user clicks the "Make a bid" button:
public function loadProduct($productId) { $this->product = Product::find($productId); if ($this->product) { $this->mountAction('placeBidding', ['product_id' => $productId]); } else { Notification::make() ->title('Product Not Found') ->danger() ->body('Unable to load the product. Please try again.') ->send(); } }
This method:
- Finds the product by ID.
- If the product is found, it mounts the placeBidding action, passing the product ID as an argument.
- If the product is not found, it sends an error notification.
5. The getActions Method
This method simply returns an array of available actions for the component:
protected function getActions(): array { return [ $this->placeBidding(), ]; }
Conclusion
By leveraging Filament's Action API and Livewire's reactivity, we've created a dynamic and user-friendly bidding system. This approach allows for real-time interaction and validation, enhancing the overall user experience.
The key to making this work smoothly is passing the product ID from the Blade template to the Livewire component, and then using that ID to mount the appropriate action with the necessary context. This pattern can be adapted for various other scenarios where you need to trigger complex actions based on user interaction with specific items in a list or grid.
Featured Products
Refdesk - Automated Reference Checking Software (SaaS)
Automated Reference Checking Software for modern organizations accelerates the hiring process by automating reference collection and verification, ensuring accurate and timely candidate assessments.
Buy for $69
Frontdesk - Visitor Management System
Gain precise insights into office visits—know who, when, and why. Instantly identify visitors not meeting entry criteria, contributing to a safe and vibrant work environment.
Buy for $29
CareCircle Manager
CareCircle Manager assists you in remembering, staying informed, organizing, and providing care to your community members.