Integrating Mapbox into a Phoenix 1.7 Project - Part 2 - Receiving events in Phoenix from Mapbox
In Part 1, we explored the basic integration of Mapbox into a Phoenix project, getting the necessary libraries installed and a map displayed. In this part, we will explore passing an event, such as the user moving a marker, back to Phoenix LiveView.
Listening to Mapbox events
In our JavaScript code where we instantiate Mapbox, let's add a draggable marker to the existing map object that we want to receive events from:
// assets/js/map.js
const marker = new mapboxgl.Marker({
draggable: true
})
.setLngLat([139.71, 35.64])
.addTo(map);
const markerMovedEvent = new CustomEvent(
'marker-moved',
{detail: marker}
);
marker.on('dragend', () => {
const mapDiv = document.getElementById('map');
mapDiv.dispatchEvent(markerMovedEvent);
});
We hook the marker dragend
event, so that every time the user finishes moving the marker, we can do something with that information.
We create a CustomEvent and bubble our dragend
event to it on the .map
div. This allows us to listen to this event in other parts of our JS code.
Setting up Phoenix hooks
To allow the Javascript running in the LiveView page to send events back to Phoenix LiveView, we need to set up Hooks. Let's keep these organised into separate JS files for cleanliness.
Create app/js/hooks.js
as follows as a place where we can maintain a list of hooks, and put in a placeholder for our new Map
hook:
// app/js/hooks.js
import Map from './hooks/map'
let Hooks = {
Map: Map,
}
export default Hooks;
Then create our Map
hook in app/js/hooks/map.js
as follows:
const Map = {
mounted() {
let map = document.getElementById('map');
if (map) {
map.addEventListener('marker-moved', (e) => {
this.pushEvent(
'marker-moved',
{ coord: e.detail.getLngLat().toArray() }
);
}, false);
}
}
}
export default Map;
On the mounted
event of the page, we look to see if the .map
div exists, and if so, listen for the marker-moved
events set up in the previous section of this tutorial.
When a marker-moved
event is received, we pass this to the Phoenix socket with this.pushEvent
, including the latitude and longitude of the marker, so that we can do some processing with this information on the server.
Enabling the Phoenix Hooks
To enable the hook we've just developed, we need to ensure it's available to the Phoenix socket, and there is somewhere in the DOM to bind it.
In your assets/js/app.js
, ensure the hooks are imported and added to the socket:
// assets/js/app.js
import {LiveSocket} from "phoenix_live_view"
import topbar from "../vendor/topbar"
import Hooks from "./hooks"
Modify the call to the LiveSocket
constructor in the same file to ensure the imported hooks are added to it:
// assets/js/app.js
let liveSocket = new LiveSocket(
"/live",
Socket,
{
params: {_csrf_token: csrfToken},
hooks: Hooks
})
Finally, in the HTML where the map is added to the page, we need to add a phx-hook
attribute. This can be added to any element within the page, but we add it to one of the map-related ones here:
<div id="map-container" class="h-screen" phx-hook="Map" phx-update="ignore">
<div id="map" class="h-full"></div>
</div>
Adding the LiveView event controller
For the marker-moved
event to be received in the Elixir code, we need to add an event handler. In the LiveView controller code:
# lib/my_project_web/live/map_live.ex
def handle_event("marker-moved", %{"coord" => [lng, lat]}, socket) do
IO.puts("Marker moved! Location: #{lng}, #{lat}")
{:noreply, socket}
end
Result!
The complete code sample can be found on GitHub.
Subscribe to my newsletter
Read articles from David Hewitt directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by