Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

Jeyziii's avatar

modal keeps closing when a livewire function is happening

When I try to search, when it loads it closes the modal. Also in the Event Payment option checkboxes and Price range input fields when the function is triggered the modal closes. How do I prevent it? TIA

searchbar.php

<?php

namespace App\Http\Livewire;

use App\Models\Event;
use Livewire\Component;

class SearchBar extends Component
{
public $query;
public $events;
public $categories;
public $eventPrice = 'all';
public $minPrice;
public $maxPrice;
public $enablePaymentTypeFilter = true;
public $enablePriceRangeFilter = true;


public function mount()
{
$this->resetData();
$this->categories = Event::pluck('category')->unique();
}

public function resetData()
{
$this->query = '';
$this->events = [];
}

public function updatedEventPrice()
{
if ($this->eventPrice === 'free') {
$this->enablePaymentTypeFilter = false;
$this->enablePriceRangeFilter = false;
$this->minPrice = null;
$this->maxPrice = null;
} else {
$this->enablePaymentTypeFilter = true;
$this->enablePriceRangeFilter = true;
}
}
public function render()
{
$this->events = Event::where('title', 'like', '%' . $this->query . '%')
->orWhereHas('schedules', function ($query) {
$query->where('venue', 'like', '%' . $this->query . '%');
})
->orWhereHas('tags', function ($query) {
$query->where('name', 'like', '%' . $this->query . '%');
})
->get()
->toArray(); 

$event_count = count($this->events);
return view('livewire.search-bar', compact('event_count'));
}
}

search-bar.blade.php

<div>

<link rel="stylesheet" href="{{ asset('css/search-modal-livewire.css') }}">

<div class="search-box" id="myBtn">
<input type="text" placeholder="Search..." class="search-input" disabled>
</div>

<!-- Modal -->
<div id="myModal" class="modal">

<div class="container">
<!-- Modal content -->
<div class="modal-content text-dark">
<span class="close">&times;</span>
<div class="modal-header">
<legend>SEARCH EVENTS</legend>
</div>

<div class="modal-body">
<!-- Search box -->
<div class="row mt-3">
<div class="col-md-12">
<span>Search</span>
<form action="{{ route('search.list') }}" method="GET">
<input class="form-control me-3" type="text" name="search" placeholder="Search events..."
wire:model.debounce.1000ms="query">
</div>
</div>

<!-- Categories dropdown -->
<div class="row mt-3">
<div class="col-md-6">
<div class="input-field">
<div class="input-select">
<select name="category">
<option value="">Categories</option>
@foreach ($categories as $category)
<option value="{{ $category }}">{{ $category }}</option>
@endforeach
</select>
</div>
</div>
</div>
</div>                

<!-- Filters -->
<div class="row mt-3">
<div class="col-md-3">
<!-- Event type checkboxes -->
<fieldset>
<legend>Event Type</legend>
<div>
<input type="checkbox" id="on-site" name="type[]" value="onsite" checked>
<label for="on-site">On-Site</label>
</div>
<div>
<input type="checkbox" id="online" name="type[]" value="online" checked>
<label for="online">Online</label>
</div>
<div>
<input type="checkbox" id="hybrid" name="type[]" value="hybrid" checked>
<label for="hybrid">Hybrid</label>
</div>
</fieldset>
</div>

<div class="col-md-3">
<!-- Event payment type radio buttons -->
<fieldset>
<legend>Event Payment Type</legend>
<div>
<input type="radio" id="all" name="payment_type" value="all" wire:model="eventPrice">
<label for="all">All</label>
</div>
<div>
<input type="radio" id="free" name="payment_type" value="free" wire:model="eventPrice">
<label for="free">Free</label>
</div>
<div>
<input type="radio" id="paid" name="payment_type" value="paid" wire:model="eventPrice">
<label for="paid">Paid</label>
</div>
</fieldset>
</div>


@if ($enablePaymentTypeFilter)
<div class="col-md-3">
<!-- Event Payment option checkboxes -->
<fieldset>
<legend>Event Payment Option</legend>
<div>
<input type="checkbox" id="gcash" name="payment_option[]" value="gcash" checked>
<label for="gcash">Gcash</label>
</div>
<div>
<input type="checkbox" id="maya" name="payment_option[]" value="maya" checked>
<label for="maya">Maya</label>
</div>
<div>
<input type="checkbox" id="paypal" name="payment_option[]" value="paypal" checked>
<label for="paypal">Paypal</label>
</div>
<div>
<input type="checkbox" id="card" name="payment_option[]" value="card" checked>
<label for="card">Card</label>
</div>
</fieldset>
</div>
@endif

@if ($enablePriceRangeFilter)
<div class="col-md-6">
<!-- Price range input fields -->
<div class="card">
<span>Price range (PHP)</span>
<div class="price-content">
<div class="row">
<div class="col">
<label>Min: </label>
<input type="number" id="min-value" name="min_price" placeholder="Min">
</div>
<div class="col">
<label>Max: </label>
<input type="number" id="max-value" name="max_price" placeholder="Max">
</div>                  
</div>
</div>
</div>
</div>
@endif
</div>

{{-- date range --}}
<div class="form-group">
<legend>Date Range</legend>
<label for="date_start" class="control-label mb-1">From</label>
<input type="date" name="date_start" value="">

<label for="date_end" class="control-label mb-1">To</label>
<input type="date" name="date_end" value="">            
</div>                                

<!-- Organizer and location input fields -->
<div class="row mt-3">
{{-- <div class="col-md-6">
<span>Organizer</span>
<input class="form-control me-3" type="" placeholder="Organizer" aria-label="">
</div> --}}

<div class="col-md-6">
<span>Location</span>
<input class="form-control me-3" type="text" name="location" placeholder="Location" aria-label="">
</div>
</div>
</div>

{{-- autocomplete list --}}
{{-- <div class="autocomplete-wrapper">
<ul class="autocomplete-list" wire:loading>
<li class="list-group-item">Searching events...</li>
</ul>

@if(!empty($query))
<div class="autocomplete-list">
@if(!empty($events))
@foreach ($events as $i => $event)
<a href="{{ url('/event') }}/{{ $event['slug'] }}" >
{{ $event['title'] }}
</a>
@endforeach
@else
<ul>
<li class="list-group-item">No results found</li>
</ul>
@endif
</div>
@endif
</div> --}}


<div class="modal-footer">
<div class="input-field">
<div class="result-count">
<span>{{ $event_count }} </span>results</div>
<div class="group-btn">
<button class="btn btn-danger" wire:click.prevent="resetData">RESET</button>
<button class="btn btn-primary">SEARCH</button>
</div>
</div>
</div>

</form>
</div>
<!-- Modal content End-->
</div>

</div>
<!-- Modal End -->
</div>

css

.modal {
    display: none;
    /* Hidden by default */
    position: fixed;
    /* Stay in place */
    z-index: 1;
    /* Sit on top */
    left: 0;
    top: 0;
    width: 100%;
    /* Full width */
    height: 100%;
    /* Full height */
    overflow: auto;
    /* Enable scroll if needed */
    background-color: rgb(0, 0, 0);
    /* Fallback color */
    background-color: rgba(0, 0, 0, 0.4);
    /* Black w/ opacity */
}

/* Modal Content/Box */
.modal-content {
    background-color: #fefefe;
    margin: 15% auto;
    /* 15% from the top and centered */
    padding: 20px;
    border: 1px solid #888;
    width: 80%;
    /* Could be more or less, depending on screen size */
}

/* The Close Button */
.close {
    color: #aaa;
    float: right;
    font-size: 28px;
    font-weight: bold;
}

.close:hover,
.close:focus {
    color: black;
    text-decoration: none;
    cursor: pointer;
}

/* price range design */
input[type=number]::-webkit-inner-spin-button,
input[type=number]::-webkit-outer-spin-button {
    -webkit-appearance: none;
    margin: 0;
}
input[type=number] {
    width: 75px;
}


/* search box design */
.search-box {
    display: flex;
    align-items: center;
    justify-content: center;
}

.search-input {
    border: 1px solid rgb(0, 0, 0);
    border-radius: 5px;
    padding: 10px 40px 10px 40px;
    width: 200px;
    background: url('https://cdn3.iconfinder.com/data/icons/google-material-design-icons/48/ic_search_48px-512.png') no-repeat scroll 10px 10px;
    background-size: 20px 20px;
    background-color: white;
}

button[type="submit"] {
    background-color: #007bff;
    border: none;
    color: white;
    padding: 10px;
    border-radius: 0 5px 5px 0;
    cursor: pointer;
}

/* autocomplete list */
.list-unstyled a { /*to unstyle the events autocomplete */
    border: none; 
}
.autocomplete-wrapper {
    position: relative; /*set the wrapper to relative*/
    display: inline-block; /*display inline block to make the button stay in the same line*/
    margin-right: 5px; /*add a margin to the right to separate the button and the input*/
}
.autocomplete-wrapper input[type="text"] {
    width: 100%; /*set the input width to 100% to take up the full width of the wrapper*/
}
.autocomplete-wrapper .autocomplete-list {
    position: absolute;
    top: 100%;
    left: 0;
    z-index: 1;
    width: 100%;
    background-color: #fff;
    border: 1px solid #ced4da;
    border-top: none;
    border-radius: 0 0 0.25rem 0.25rem;
    max-height: 200px;
    overflow-y: auto; /* add overflow-y to enable vertical scrolling when needed */
}

.autocomplete-wrapper .autocomplete-list a {
    display: block; /*display the list items as blocks*/
    padding: 0.5rem 1rem; /*add some padding*/
    color: #333; /*set the text color*/
    text-decoration: none; /*remove the underline*/
}
.autocomplete-wrapper .autocomplete-list a:hover {
    background-color: #f0f0f0; /*set a background color on hover*/
}
0 likes
2 replies
shahr's avatar

It seems like the Livewire component is causing the modal to close when its function is triggered. One possible solution for preventing this behavior is by using JavaScript to stop the event from bubbling up to the modal.

You can add an @click event listener to the search button and prevent the default behavior of the click event. This way, the Livewire function will be executed without closing the modal.

Here's an example of how you can modify the search-bar.blade.php file with the suggested changes:

html

<!-- Search bar -->
<div class="row mt-3">
    <div class="col-md-12">
        <span>Search</span>
        <<form action="{{ route('search.list') }}" method="GET">
            <input class="form-control me-3" type="text" name="search" placeholder="Search events..."
                wire:model.debounce.1000ms="query">
        </form>
    </div>
</div>

<!-- Filters -->

<!-- Price range input fields -->
@if ($enablePriceRangeFilter)
<div class="row mt-3">
    <div class="col-md-6">
        <div class="card">
            <span>Price range (PHP)</span>
            <div class="price-content">
                <div class="row">
                    <div class="col">
                        <label>Min: </label>
                        <input type="number" id="min-value" name="min_price" placeholder="Min">
                    </div>
                    <div class="col">
                        <label>Max: </label>
                        <input type="number" id="max-value" name="max_price" placeholder="Max">
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
@endif

<!-- Search button -->
<div class="modal-footer">
    <div class="input-field">
        <div class="result-count">
            <span>{{ $event_count }} </span>results
        </div>
        <div class="group-btn">
            <button class="btn btn-danger" wire:click.prevent="resetData">RESET</button>
            <button class="btn btn-primary search-btn">SEARCH</button>
        </div>
    </div>
</div>

@push('scripts')
<script>
    document.querySelector('.search-btn').addEventListener('click', function (e) {
        e.preventDefault();
        e.stopPropagation();
        @this.call('render');
    });
</script>
@endpush

In this example, we added a search-btn class to the search button and attached an event listener that listens for the click event. When the user clicks the button, the event listener prevents the default behavior of the click event and stops it from bubbling up to the modal.

Then, we used the @this.call directive to call the render method on the Livewire component. This way, when the user clicks the search button, the Livewire function will be executed without closing the modal.

1 like
Jeyziii's avatar

@qom Sorry for the poor explanation, but when I type into the search box and the query triggers without clicking the search button, the modal closes. The modal is also closed when the updatedEventPrice triggered when I changed the event payment radio button to free and vice versa.

Please or to participate in this conversation.