JS map jumps on page whenever liveview updated() is triggered

As some of you can tell I have been diving deep into liveview and particularlly the integration with JS via hooks. So far most of it makes sense and I have been really impressed by just how easy it is to get data into the DOM and update efficiently. I have one very strange problem where anytime updated() is called in my JS hook, my JS map shifts on the page and the text on the lower right corner gets bigger. I created a fresh liveview app to reproduce the issue and it still happens, all I need in my updated() call is a console.log(“test”) so that I know it is triggered, there is nothing else in there and the map shift still occurs.

Below I will detail the steps I used to build a fresh app and get a map up and running using hooks. Everything works perfectly, I can send data properly into the updated() call as well, its just this weird shift of the map is occurring right when updated() is called. Sorry this is a bit hard to reproduce as you need a mapboxgl key to get the map up but I did my best to show the steps below.

This app displays a map and whenever a location is clicked, that longitude and latitude are sent as params to the handle_event on the server, which then update the test field in the assigns, which then triggers updated() as the map div takes data-test.

Create a new app with liveview

mix phx.new mapping --live

Install mapboxgl

cd assets
npm install --save mapbox-gl

Add mapboxgl required css to root template (root.html.leex)

<link href='https://api.mapbox.com/mapbox-gl-js/v1.11.0/mapbox-gl.css' rel='stylesheet' />

Add some width/height css for the map div

#map {
  width: 800px;
  height: 600px;
}

page_live.ex

defmodule MappingWeb.PageLive do
  use MappingWeb, :live_view

  def mount(_params, _session, socket) do
    {:ok, assign(socket, test: [])}
  end

  def handle_event("map_click",
    %{"location" => %{"lat" => latitude, "lng" => longitude}},
    socket) do

    {:noreply, assign(socket, test: %{latitude: latitude, longitude: longitude})}
  end
end

page_live.html.leex

<div id="map" phx-hook="Map" data-test="<%= Jason.encode!(@test) %>" phx-update="ignore"></div>

Add in the Hook in the apps.js file

import "phoenix_html"
import { Socket } from "phoenix"
import NProgress from "nprogress"
import { LiveSocket } from "phoenix_live_view"
import mapboxgl from 'mapbox-gl';

let Hooks = {}
Hooks.Map = {
    mounted() {
        mapboxgl.accessToken = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';

        var map = new mapboxgl.Map({
            container: 'map',
            style: 'mapbox://styles/mapbox/outdoors-v11', // stylesheet location
            center: [-122.4376, 37.7577],
            zoom: 8
        });

        const view = this;
        map.on('click', function (e) {
            view.pushEvent("map_click", { location: e.lngLat })
        });
    },
    updated() {
        console.log("UPDATED")
    }
}

let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
let liveSocket = new LiveSocket("/live", Socket, { params: { _csrf_token: csrfToken }, hooks: Hooks })

Figured out what was going on. For anyone else who may find this in trying to get mapboxgl working with their phoenix app. You need to add class="mapboxgl-map" to your div where you put the map. If you dont include that when updated() is called, the class is removed from the initial div which has it and that causes the crazy formatting (as the css is removed).

2 Likes