Best practice use data in javascript (thus passing data)

Haven’t found a topic on the elixir forum regarding this, only on stackoverflow. If it exists already, please point me in the right direction and I’ll delete this post. Because the response was limited, I was wondering if the Phoenix experts here could provide their opinions regarding passing data from the controller to your JS.

A use case that I’m facing now:
Building a timeline-ish feature with an edit page. On the edit page, i want to let the user upload images asynchronously. (so that if they edit something in the text field, the explanation on the timeline, and then upload an image it doesn’t need to refresh) But I need the article id to make the asynchronous call.

As far as i know there are 2 options:

  • work with data attributes (link)
  • put a tag in your template, and assign a variable where you put your json encoded (elixir) variable so that you can access it in your javascript.

This is a problem with multiple frameworks, and I’d be truly happy if some sort of best-practice could be formed.

Thank you in advance!

I prefer to pass data to js with data attributes, it’s easy.

I have something like this in my template

<div 
  id="app" 
  data-src="<%= display_path @picture, :large %>"
  data-id="<%= @picture.id %>"
  data-token="<%= @token %>"></div>

and I retrieve like this in app.js.

const root = document.getElementById('app');
if (root) {
  const { src, id, token } = root.dataset;
  console.log(src, id, token);
}
6 Likes

@kokolegorille
I’m wondering what the best practice is for passing data from javascript to the template. In my project I’m using the browser API to user location data which I need to pass back to the server. At this point the user does not have a session and is anonymous. I want to pass the location data to a registration page which uses a get request to show a new action.

I had considered using a hidden form field (setting the hidden value with Javascript) but this did not seem right on a get request and to be honest I’m not sure if this would actually work.

I also considered setting a cookie and then retrieving it in the registration controller but again this does not seem right and would not work if cookies are disabled by the user.

I’ve searched through the documentation and have not come across anything that addresses this use case.

It appears that Live View has Hooks which allows this inter-operability but changing my page to Live View would not really be appropriate.

Any help would be appreciated.

Hello and welcome,

It is a little bit more complicate to pass js data to the server, because http is not bidirectional.

Are You sure You cannot do all client side? recently I had to display datetime in the browser timezone. It was simpler to add some class to datetime container (“utc_to_local”) and make the change client side only.

But if You want to make it server side, I think the easiest solution would be to pass the locale, like new?locale=whatever. Because new is a get request.

You could use a plug to set a session_id.

This way, You could pass data to the server with this session_id, and retrieve this data server side

It’s not even needed, and it is nice to see how it is done in liveview, which use websocket under the hood. You can set params to the socket, and retrieve them in liveview.

let liveSocket = new LiveSocket("/live", Socket, {
  params: {
    _csrf_token: csrfToken,
    locale: Intl.NumberFormat().resolvedOptions().locale,
    timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
    timezone_offset: -(new Date().getTimezoneOffset() / 60),
  }
});

# In liveview mount

    if connected?(socket) do
      Logger.info("SOCKET PARAMS: #{inspect get_connect_params(socket)}")
    end

# And the result

  [info] SOCKET PARAMS: %{
    "_csrf_token" => "...",
    "_mounts" => 0,
    "_track_static" => ["..."],
    "locale" => "fr",
    "timezone" => "Europe/Zurich",
    "timezone_offset" => 2
  }

So You could use websocket, but it does not seems You need this, You just want to localize your login form. => new?locale=whatever and retrieve the locale server side.

Use an id to the new link, and add locale params to the path of this link with js.

3 Likes

Hello @kokolegorille

I’m very grateful for your quick and comprehensive reply. I think your explanations are very clear and understandable.

I’ve experimented using passing the locale as a parameter (setting it with the browser URL API) and this works just fine - I can then match on parameter in the controller and use the data as needed.

Once again, thank you for taking the time to reply, it’s very much appreciated.

1 Like