1 Like
Hello @ymihn-nhi,
Thanks for bringing this to our attention! Indeed, this article was written with Laravel Livewire v2. I’ll get a banner up in that article to let readers know about this.
Thanks again!
1 Like
No worries, I have a solution for v3 if you guys want to take a look. Otherwise, have a great day!
1 Like
Hello,
I’m very interested in knowing how this can be applied to Livewire3. I’d appreciate your help. Thank you.
Sure, here is the solution!
within your blade view:
<div>
<!-- Map Container -->
<div class="bg-white rounded-lg shadow">
<div wire:ignore id="map" class="w-full h-[400px]"></div>
</div>
<!-- Javascript code -->
@script
<script>
/* Google Auth Boostrapper */
(g => {
var h, a, k, p = "The Google Maps JavaScript API", c = "google", l = "importLibrary", q = "__ib__",
m = document, b = window;
b = b[c] || (b[c] = {});
var d = b.maps || (b.maps = {}), r = new Set, e = new URLSearchParams,
u = () => h || (h = new Promise(async (f, n) => {
await (a = m.createElement("script"));
e.set("libraries", [...r] + "");
for (k in g) e.set(k.replace(/[A-Z]/g, t => "_" + t[0].toLowerCase()), g[k]);
e.set("callback", c + ".maps." + q);
a.src = `https://maps.${c}apis.com/maps/api/js?` + e;
d[q] = f;
a.onerror = () => h = n(Error(p + " could not load."));
a.nonce = m.querySelector("script[nonce]")?.nonce || "";
m.head.append(a)
}));
d[l] ? console.warn(p + " only loads once. Ignoring:", g) : d[l] = (f, ...n) => r.add(f) && u().then(() => d[l](f, ...n))
})({
// this api key is exposed to client
key: "{{ config('google-map.api_key') }}",
v: "weekly",
// Use the 'v' parameter to indicate the version to use (weekly, beta, alpha, etc.).
// Add other bootstrap parameters as needed, using camel case.
});
let map;
let markers = [];
let infoWindow;
function clearMarkers() {
markers.forEach(marker => {
marker.map = null;
});
markers = [];
}
// Helper function to open info window with detailed content
function openDetailedInfoWindow(marker, placeData) {
// Close any open info window first
if (infoWindow) {
infoWindow.close();
}
// Get the rendered template from the server
$wire.renderInfoWindowTemplate(placeData)
.then(content => {
// Update the info window with the rendered content
infoWindow.setContent(content);
infoWindow.open(marker.map, marker);
})
.catch(error => {
console.error("Error rendering template:", error);
infoWindow.setContent(`
<div class="p-4 text-center">
<p class="text-red-500">Error loading content</p>
</div>
`);
});
}
$js('showPlaceInfo', (place) => {
place = JSON.parse(place)
// map through markers to get the one with the correct title
const marker = markers.find(marker => marker.placeID === place.id)
if (marker) {
openDetailedInfoWindow(marker, place)
} else {
// handle not find marker
}
})
// Function to create markers
async function createMarkers(obj) {
const {locations, lat, lng} = obj;
// console.log({locations})
const {AdvancedMarkerElement} = await google.maps.importLibrary(
"marker",
);
let latitude = parseFloat(lat);
let longitude = parseFloat(lng);
// Check if the parsed values are valid numbers
if (isNaN(latitude) || isNaN(longitude)) {
console.error('Invalid lat/lng values:', lat, lng);
return;
}
// Try setting center right away
try {
map.setCenter({lat: latitude, lng: longitude});
const userMarker = new AdvancedMarkerElement({
position: {lat: latitude, lng: longitude},
map,
title: 'You are here',
gmpClickable: true,
content: pinSvg,
});
userMarker.placeID = 'user';
userMarker.addListener("click", () => {
infoWindow.setContent('You are here');
infoWindow.open(map, userMarker);
});
markers.push(userMarker);
} catch (e) {
console.error('Error setting map center:', e);
}
locations.forEach((loc, i) => {
const {latitude, longitude, title} = loc;
const position = {lat: latitude, lng: longitude};
const marker = new AdvancedMarkerElement({
position,
map,
title: `${title}`,
gmpClickable: true,
});
marker.placeID = loc.id;
// Add click listener for info window
marker.addListener("click", () => {
openDetailedInfoWindow(marker, loc)
});
// Store marker for later removal if needed
markers.push(marker);
});
if (markers.length > 0) {
const bounds = new google.maps.LatLngBounds();
markers.forEach(marker => {
bounds.extend(marker.position);
});
map.fitBounds(bounds);
}
return markers;
}
async function initMap() {
// Request needed libraries.
const {Map, InfoWindow} = await google.maps.importLibrary("maps");
map = new Map(document.getElementById("map"), {
zoom: 12,
center: {lat: $wire.lat, lng: $wire.lng},
mapId: "4504f8b37365c3d0",
});
infoWindow = new InfoWindow();
}
// Initialize map on first page load
initMap();
// Register event listener
Livewire.on('location-updated', (obj) => {
clearMarkers();
createMarkers(obj);
});
</script>
@endscript
</div>
- Make sure your Livewire component dispatch ‘location-updated’ event, has
$lat
,$lng
variable, hasrenderInfoWindowTemplate
method - Make sure you have a button that will trigger javascript function showPlaceInfo, eg,
<button wire:click="$js.showPlaceInfo('{{ json_encode($place) }}')"
- Make sure you created a config file
google-map
to set public environmentapi_key