Append user submission (front-end javascript response) to data structure

Hi, I’m looking for some guidance on how to tie two components in my site together.

What I’m trying to do: Use a button to request user location (through geolocation api), which appends the user’s coordinates to a json file. The json is then used to populate a map with user’s location pins.

  1. I have the button working, and retrieving the user’s coordinates.
  2. I have the map working, and reading a json of coordinates (for placeholder purposes, the current json is pulling from a similar content representation I have with my feed posts – each has an attached location).
  3. How can I append the user’s coordinate details into the json file?

addpins.js

export const addPins = async () => {
  const button = document.getElementById("request-location");

  button.addEventListener("click", () => {
    if ("geolocation" in navigator) {
      navigator.geolocation.getCurrentPosition((position) => {
        // Get the latitude and longitude
        const latitude = position.coords.latitude;
        const longitude = position.coords.longitude;

        console.log("Latitude:", latitude);
        console.log("Longitude:", longitude);
      });
    } else {
      console.log("Geolocation is not supported");
    }
  });
};

export default addPins();

map.js

import mapboxgl from "mapbox-gl";
import map from "@/assets/scripts/map";

export function Map() {
  function addPosts(map, data) {
    data.forEach((eventData) => {
      const coordinates = [eventData.location?.lon, eventData.location?.lat];
      const title = eventData.title;
      const url = eventData.url;

      if (
        coordinates &&
        !Number.isNaN(parseFloat(coordinates[0])) &&
        !Number.isNaN(parseFloat(coordinates[1]))
      ) {
        const el = document.createElement("div");
        el.className = "marker";
        new mapboxgl.Marker(el, { rotation: 0 })
          .setLngLat(coordinates)
          .setPopup(
            new mapboxgl.Popup({ offset: 25, closeButton: false }).setHTML(
              `<a href="${url}">${title}</a>`
            )
          )
          .addTo(map);
      }
    });
  }

  fetch("/home.json")
    .then(function (response) {
      return response.json();
    })
    .then(function (data) {
      addPosts(map, data);

      const transformedEventData = data.map((event) => ({
        start: event.date + "T00:00:00",
        end: event.date + "T00:00:00",
        name: event.title,
      }));
    })
    .catch(function (error) {
      console.error("Error loading or transforming JSON data:", error);
    });
}

I’ve been trying to use the page controller to listen for AJAX or POST events, using something like the following– which didn’t work out for me…& have also tried tying it into the Uniform plugin, to no avail.

controllers/guestbook.php

if ($kirby->request()->is('POST')) {
        $form->logAction([
            'file' => kirby()->roots()->content() . '/guestbook/locations.json',
            'escapeHtml' => false,
        ]);
    }

Any guidance very much appreciated, thank you! I found this thread from a few years back, but it is slightly beyond my somewhat-limited programming knowledge.

why not use standard js geolocation api and create a small ajax request to backend forwarding an id stored in php session or kirbys user id along with the result of the js geo api?

I’d do an ajax post request to a route, which then handles the data processing

I think I’m one step closer, but my actual posts aren’t formatting or appending properly…I’m just ending up with an ever-nesting set of brackets on each attempt to post to the json.

config.php

'routes' => [
        [
            'pattern' => 'ajax/post',
            'method' => 'POST',
            'action' => function () {
                $postData = $_POST;
                $path = kirby()->roots()->content() . '/guestbook/locations.json';

                // Load existing data if the file exists
                $existingData = [];
                if (file_exists($path)) {
                    $existingData = json_decode(file_get_contents($path), true);
                }

                // Append the new data to the existing data array
                $existingData[] = $postData;

                // Save the updated data as JSON
                $jsonData = json_encode($existingData);

                // Save the JSON data to the file
                file_put_contents($path, $jsonData);
            }

        ],
    ],

It successfully makes a new json file if one doesnt exist, and appends to the current one if it does. Nothing actually gets added though, outside of nested brackets.

pins.js

...
const data = {
    latitude: latitude,
    longitude: longitude,
};

try {
    // Send a POST request with the data
    const response = await fetch("/ajax/post", {
    method: "POST",
    headers: {
        "Content-Type": "application/json",
    },
    body: JSON.stringify(data),
    });

    if (response.ok) {
    console.log("POST request sent successfully");
    } else {
    console.log("Failed to send POST request");
    }
} catch (error) {
    console.log("Error:", error);
}
...

Console outputs Failed to send POST request.

This feels close, and I’m sure there’s a small problem (problably multiple) that just isnt jumping out me because I’m not familiar enough with the grander methods here…

This won’t work, use $postData = kirby()->request()->data();

Your route doesn’t return anything on failure or success, so you can wait long for a response…

So do something like

return [
  'success' => true, //
];

etc. depending on result.

1 Like

Thanks for pointing in the right direction here, all is working as expected now.

// addPins.js

...
try {
          const response = await fetch("/ajax/post", {
            method: "POST",
            headers: {
              "Content-Type": "application/json",
            },
            body: JSON.stringify(data),
          });

          const responseData = await response.json();

          if (responseData.success) {
            console.log(responseData.message);
          } else {
            console.log("Failed to send POST request");
          }
        } catch (error) {
          console.log("Error:", error);
        }
});
...

// config.php

'routes' => [
        [
            ...

                // Prepare the response
                $response = [
                    'success' => true,
                    'message' => 'Location request sent successfully',
                ];

                // Return the response as JSON
                return json_encode($response);
            }
        ],
    ],