This article is obsolete in Livewire 3

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! :blush:

1 Like

Hello,
I’m very interested in knowing how this can be applied to Livewire3. I’d appreciate your help. Thank you.

:slightly_smiling_face:

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>
  1. Make sure your Livewire component dispatch ‘location-updated’ event, has $lat, $lng variable, has renderInfoWindowTemplate method
  2. Make sure you have a button that will trigger javascript function showPlaceInfo, eg,<button wire:click="$js.showPlaceInfo('{{ json_encode($place) }}')"
  3. Make sure you created a config file google-map to set public environment api_key