How to use Google Maps with Phoenix LiveView

Tiago Duarte
Aug 3 · 8 min read
Image for post
Image for post
How to use Google Maps with Phoenix LiveView

Howdy dear reader 🖖🏽, hope you are safe and healthy during these troubled times.
During the past 3 years, we have developed a few projects using the Phoenix Framework for Elixir. And as soon as LiveView was announced, we could not wait to use it!

We have been building a platform where using LiveView made a lot of sense, since we wanted some of our features to use an interface that could be updated in real-time. For example, we wanted administrators of the platform to be able to see, on a map, the sightings that the users of the app were reporting on certain places, as they happen. Let’s call it a live map.

Understanding the technology 🤓

Phoenix LiveView

LiveView provides rich, real-time user experiences with server-rendered HTML.

The LiveView programming model is declarative: instead of saying “once event X happens, change Y on the page”, events in LiveView are regular messages which may cause changes to its state.
Once the state changes, LiveView will re-render the relevant parts of its HTML template and push it to the browser, (…) LiveView does the hard work of tracking changes and sending the relevant diffs to the browser.

Google Maps for javascript

The Maps JavaScript API lets you customise maps with your own content and imagery for display on web pages and mobile devices.

For you to get up and running, and have a page that displays a map centered in Sydney, you just need to do the following:

Challenge 🤔

Solution 💡

a) Initial Setup

b) Adding the map to a LiveView page

If you now run the project (mix phx.server), you will see that the map shows for a brief moment, disappearing right after. Where did the map go? 🤔

c) Keeping the map on the LiveView page

A LiveView begins as a regular HTTP request and HTML response, and then upgrades to a stateful view on client connect, guaranteeing a regular HTML page even if JavaScript is disabled. Any time a stateful view changes or updates its socket assigns, it is automatically re-rendered and the updates are pushed to the client.
(…)
After rendering the static page, LiveView connects from the client to the server where stateful views are spawned to push rendered updates to the browser, and receive client events via phx- bindings. Just like the first rendering, mount/3 is invoked with params, session, and socket state, where mount assigns values for rendering.

If you want to go deeper into the topic, this video explains very well how the inners of LiveView works.

In our case, what is happening is that:

  1. There is an initial page render
  2. The google maps script is loaded and calls our initMap function
  3. The initMap function initialises the map. Which causes the div with the id="map" to be filled with all the necessary markup to represent the map
  4. At the same time, the webpage connects to the LiveView on the server. And as the documentation states: “Just like the first rendering, mount/3 is invoked with params, session, and socket state, where mount assigns values for rendering.”
  5. The LiveView (JavaScript) on the browser side, then does the diffing of what should the HTML be and what it is now
  6. And, surprise, surprise 🎁 ! The current HTML contains the map markdown, which wasn’t there when the page initially rendered, and it also wasn’t added by the LiveView. As such the LiveView on the client-side reverts that change 😭

The good part is that the creators of LiveView thought about these cases and give you the possibility to control the patching mechanism:

A container can be marked with phx-update, allowing the DOM patch operations to avoid updating or removing portions of the LiveView, or to append or prepend the updates rather than replacing the existing contents. This is useful for client-side interop with existing libraries that do their own DOM operations.
(…)
The “ignore” behaviour is frequently used when you need to integrate with another JS library.

In practical terms, we only need to add a phx-update="ignore" to the map container, to inform LiveView that the contents of that container are controlled by us.
You should now see the following result:

Image for post
Image for post
Google Map rendered in a LiveView page

d) How to dynamically push elements to the map from the server

The end result should something like this:

Image for post
Image for post
Adding server side generated markers to the live map

To sum up the important parts, what we did was:

d.1) Create a javascript hook

We are making use of the newly added handleEvent / pushEvent feature of hooks. This method is available since LiveView version 0.14:

The hook can push events to the LiveView by using the pushEvent function and receive a reply from the server via a {:reply, map, socket} return value. The reply payload will be passed to the optional pushEvent response callback.

Communication with the hook from the server can be done by reading data attributes on the hook element, or by using push_event on the server and handleEvent on the client.

d.2) Use the javascript hook on the map markup

In order to use / attach the newly created hook we just had to do the following:

<section class="row" phx-update="ignore" phx-hook="MapSightingsHandler">

As soon as the element is mounted and the LiveView Genserver pushes an event with the name new_sighting , the handleEvent callback is triggered and a map marker is added.

d.3) Update the LiveView Genserver to push the events / sightings

The server part that actually receives orders to create a random sighting, via an event sent by a phx-click=”add_random_sighting” on a button for the sake of simulation, and pushes the event to the browser’s socket:

If you apply all the changes and run the code you should now be able to randomly create markers on your map.

Improvements 👆🏾

How to display on the map the already stored sightings?
One might think “this seems very easy, on the first mount we could send / schedule a pushEvent!”.
The problem here is that the Google Map script might be rendered before or after we call that pushEvent. Meaning that if the script is loaded after, the handleEvent function that creates markers won’t have access to the map and as such it will fail and won’t render the markers!

Properly store and broadcast sightings creation, updates and deletions?
We only handle sightings additions and we don’t even store them on a database nor broadcast it to all users.
How would one receive a new sighting, store it in a database and then broadcast this information to all users?

How to better structure the code?
In order to keep the article focused, once again we decided to go for the shortest solution and focus on the technology itself.
For instance, we are exposing the map object to the hook by adding it directly to the window. Please don’t do this in production!

Feel free to tackle the problems mentioned and do pull requests with your suggested solutions. We will be very happy to learn from you as well! 👩🏾‍🎓

Conclusion

The biggest downside we still have with LiveView is the code structuring / organisation. We haven’t yet found a structure that we feel 100% confident with, so we are continuing to iterate until we get there.
The community is already working on solutions for this, and there are already libraries like surface that address this issue. Surface is on our “test / research / learning” list and we will certainly give it a try.

Also LiveView is still in an alpha stage. We have to keep upgrading the dependency quite often, as new releases with breaking changes are made available. The bright side of this is that it keeps getting better with every new release! 🥇

Thank you for reading!

In case you don’t know, Coletiv is a software development studio from Porto specialized in Elixir, iOS, and Android app development. But we do all kinds of stuff. We take care of UX/UI design, web development, and even security for you.

So, let’s craft something together?

Coletiv

Thoughts, dreams and rants about technology and work life…

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store