I believe the issue is that $locations is a private property and private properties are not persisted between updates. See the docs - https://laravel-livewire.com/docs/2.x/properties#introduction
Livewire + Google Maps api: Why is my collection empty?
I am building a livewire component that renders a Google maps with markers. I have many filters to search for locations depending on preferences. All of these work fine.. But when I wanted to add a locations count, I had an error: count(): Argument #1 ($var) must be of type Countable|array, null given.
At first the count appears fine, but a second later it jumps to this error. If I remove the {{$locations->count()}} and just echo out {{$locations}}, a list of locations appears and then a second later disappears. So the collection is reset to null.
Who can help me understand why? For readability I have removed most functions from controller en view. Only the bug remains. I was able to pinpoint the issue is only there when the updateMapPosition($latlng, $zoom) happens.
My controller:
<?php
namespace App\Http\Livewire;
use App\Models\Country;
use Illuminate\Support\Facades\DB;
use Livewire\Component;
class Test extends Component
{
protected $listeners = ['updateMapPosition'];
private $locations;
public $zoom;
public $latlng;
public $country;
public $queryString = [
'zoom' => [
'except' => '3',
'as' => 'z',
],
'latlng' => [
'except' => '15,0',
'as' => 'p',
],
'country' => [
'except' => '',
'as' => 'c',
],
];
public function mount()
{
$this->zoom = request('z') ?? '3';
$this->latlng = request('p') ?? '15,0';
$this->country = request('c') ?? '';
$this->locations = $this->getLocationsFromDB();
}
public function filterLocations()
{
$this->locations = $this->getLocationsFromDB();
}
public function updateMapPosition($latlng, $zoom)
{
$this->latlng = $latlng;
$this->zoom = $zoom;
}
// get the filtered collection
private function getLocationsFromDB()
{
$db = DB::table('locations AS l')
->select('l.id', 'l.name', 'l.latitude', 'l.longitude')
->where('l.status', 1);
if ($this->country) {
$db
->join('countries AS c', 's.country_id', '=', 'c.id')
->where('c.id', $this->country);
}
$locations = $db->distinct()->get();
return $locations;
}
public function render()
{
return view('livewire.test', [
'locations' => $this->locations,
'countries' => Country::has('locations')->get(),
]);
}
}
Template:
<div id="wrapper">
<div id="filter">
{{$locations->count()}}
<div class="filter">
<label class="h3" for="country">Search by country</label>
<select wire:model="country" wire:change="filterLocations" name="country" id="country">
<option value="" selected>-- all countries ---</option>
@foreach ($countries as $country)
<option value="{{ $country->id }}">{{ $country->name }}</option>
@endforeach
</select>
</div>
</div>
<div id="map-container">
{{ $locations }}
<div wire:ignore id="map"></div>
</div>
</div>
@push('scripts')
<script>
let map;
let markers = [];
function initMap() {
let [latitude, longitude] = @js($latlng).split(',');
const latLng = {
lat: parseFloat(latitude),
lng: parseFloat(longitude)
};
map = new google.maps.Map(document.getElementById("map"), {
center: latLng,
zoom: parseInt({{ $zoom }}),
scrollwheel: true,
});
// listen to map changes & update map position
map.addListener("idle", function() {
let zoom = map.getZoom();
let latLng = map.getCenter().lat() + "," + map.getCenter().lng();
changeMapPosition(zoom, latLng);
});
};
// update map position
function changeMapPosition(zoom, latLng) {
Livewire.emit("updateMapPosition", latLng, zoom);
}
</script>
<script
src="https://maps.googleapis.com/maps/api/js?key={{ config('app.google_api_key') }}&language=en&callback=initMap">
</script>
@endpush
Thanks for your insights!
Please or to participate in this conversation.