willc0de4food

willc0de4food

Howdy,

I’m struggling with something that seems like it should be simple. I would like to fetch some data via async when the page loads, and then once loaded, graph that data with Highcharts. Everything I’ve read has indicated that you need to setup a hook that will allow you to pass the data from the server to the JS, and you use a handle_event to trigger push_event() with the data. I don’t have a user interaction to trigger the handle_event, I just want the graph to load once the data is ready. I tried adding the push_event() to handle_async , but nothing happens in the JS hook. I have added a dbg() line to verify that the data is being received in the handle_async.

What am I missing? :sweat_smile:

Some code:

def mount() do
  ...
  |> start_async(:points, fn -> Tokens.generate_chart_data() end)
  ...
end

  def handle_async(:points, {:ok, points}, socket) do
    dbg(points)
    {:noreply, push_event(socket, :points, %{points: points})}
  end

  def render(assigns) do
    ~H"""
    ...
    <div class="mt-4 flex flex-row w-full">
        <div id="graph" phx-hook="Graph"></div>
    </div>
    """
  end
var Hooks = {};
Hooks.Graph = {
  mounted() {
	logToConsole('mounted');
  },
  updated() {
  	logToConsole('updated');
  	this.handleEvent("points", ({points}) => logToConsole(points);
  }
}
let logToConsole = (action) => {
	console.log('*********************', action);
}

Showing Posts 1 to 7

cmo

cmo

Firstly, you need to put this.handleEvent("points", ({points}) => logToConsole(points); in mounted.

You can set it up so that the process receives a message every x seconds, which you handle in handle_info and can trigger the start_async from there.

You can send the message every x seconds with :erlang.send_interval(Time, Destination, Message) or trigger it each time you process the message in handle_info/handle_async. That is Process.send_after(...)), called once in mount and then again in handle_info

If the data is on some publication you could subscribe to that in your liveview.

willc0de4food

willc0de4food OP

Hello! Thanks for that info. Do I need this.handleEvent in both mounted() and updated()? Or just in mounted()?

The data is static, on my local server. Basically just a bunch of numbers and timestamps that are retrieved, compiled and sent to the front end. So once the data has been retrieved, it won’t change.

Sorry, I’m new to Elixir & Phoenix, where in the liveview would I setup the :erlang.send_interval()?

cmo

cmo

:erlang.send_interval would go in mount

this.handleEvent adds an event handler, so you only need to it in mounted().

If the data is quick to retrieve, you can do the work in handle_info and not worry about the async retrieval.

willc0de4food

willc0de4food OP

Update: I got it working without hooks! It was as straight forward as it seemed like it should be, I just didn’t know what I didn’t know. The final working product:
live view:

  def mount(params, _session, socket) do
    ...
    socket =
      socket
      ...
      |> start_async(:points, fn -> Tokens.generate_chart_data() end)
    {:ok, socket}
  end

  def handle_async(:points, {:ok, points}, socket) do
    {:noreply, push_event(socket, "graph", %{points: points})}
  end

  def render(assigns) do
    ~H"""
      ...
          <div id="graph"></div>
    """
  end

app.js:

window.addEventListener(
  "phx:graph",
  e => {
  	if (e.detail?.points) {
  		Highcharts.chart('graph', { ... });
  	}
  }
)

That’s it :slightly_smiling_face:

cmo

cmo

I’d still go with a hook as often you want to dispose of the chart in destroyed and/or modify it in update and push the event to the specific chart.

So sorry for the timer stuff, I had realtime chart on the brain from a post on the discord and conflated the two.

willc0de4food

willc0de4food OP

Ok, that makes more sense w/the timer stuff. I was wondering, but figured I’d give it a try just to see if I could get ANYTHING to work :sweat_smile:
I’m still unclear as to how I would implement this with hooks as there’s no user interaction :thinking:

cmo

cmo

To do the initial one, you push an event in mount if it is a liveview or in update if it is a live component.

For example:

defmodule MyWeb.Components.Charts.EchartComponent do
  use MyWeb, :surface_live_component

  @doc "E-Charts `option` configuration map"
  prop option, :map, required: true

  @doc "Merge the option with the existing one (`true`), or replace it (`false`)?"
  prop merge, :boolean, default: true

  prop class, :css_class, default: "flex h-full w-full"

  def push_loading_event(socket, chart_id) when is_binary(chart_id) do
    push_event(socket, "echarts:#{chart_id}:loading", %{})
  end

  def push_update_event(socket, chart_id, %{"option" => _} = params) when is_binary(chart_id) do
    params = Map.put_new(params, "merge", true)
    push_event(socket, "echarts:#{chart_id}:update", params)
  end

  def push_init_event(socket, chart_id, %{"option" => _} = params) when is_binary(chart_id) do
    params = Map.put_new(params, "merge", true)
    push_event(socket, "echarts:#{chart_id}:init", params)
  end

  def update(%{id: id, option: option, merge: merge} = assigns, socket) do
    {:ok,
     socket
     |> assign(assigns)
     |> push_init_event(id, %{"option" => option, "merge" => merge})}
  end

  @impl true
  def render(assigns) do
    ~F"""
    <div phx-hook="Echarts" id={"echarts-#{@id}"} phx-update="ignore" class={@class} data-id={@id} />
    """
  end
end

In a liveview that uses this, I push “loading” on mount and when params are changed, I push “loading” again and call start_async to fetch the new data async. When it lands, I push an “update” event with the new data. Echarts has a built in show/hide loading indicator function which I’m using. You could a similar thing with the <.async_result> component.

— All posts loaded —

Where Next? Top

Trending in Questions Top

stjefim
Hello! Suppose you are building workflow (order / task / payment) processing system with the following requirements: Each workflow con...
New
water
I’m aware of 2 such projects, are there others? git: GitHub - team-alembic/realworld: A fullstack Phoenix LiveView application with back...
New
peck
Hi everyone. My team and I have been working on a fairly modest app based around video streaming and chat, but we’ve landed a customer t...
New
bradley
I really like the adapter patterns that ecto, nebulex, waffle, etc. use and would love find something similar for a key management servic...
New
unaware8150
Hello folks! So at work, we are seeing some situations where we have to define some “fixed” strings that are used across the codebase in...
New
Alvinkariuki
How Can I Optimise Compile Time Dependencies I have been building an elixir application for about 2 years now. Many modules and files ha...
New
leoschweizer
Hi, we run different LLM models across a cluster, with each node loading a configurable subset. Model-specific queues let nodes consume ...
New

Other Trending Topics Top

mudasobwa
I fully migrated to my own harness from Anthropic/Gemini and I think it’s time to share it. Welcome DSH, the DeepSeek Harness, fully writ...
New
KristerV
Hey. Is there anyone here who creates agents in their apps? Not talking about using agents, but creating them. I’m finding it pretty diff...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
juhalehtonen
There has been a thread to discuss the Stack Overflow Developer Survey on this forum every year since 2018, so here’s yet another one for...
New
webofbits
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself. My main conc...
#ai
New
budgie
I love Elixir. It’s one of 2 programming languages I’ve ever fallen in love with. But I don’t use it anymore. Serverless was the promis...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews