AHBruns

AHBruns

LiveView Async With Regular Processes

So, LiveView 0.20 brought an async processing API which is a thin wrapper around tasks. It’s a very nice convenience, but the main thing that intrigues me is support within live_components. This is the first time that live components have been able to receive system messages directly. Anyone who’s tried to do async data loading in a live component knows what I’m talking about.

This is great, but it’s a very narrow API. It lets you perform a single async task and get its results. I’m proposing a more generic version of this API. Currently we have

def mount(%{"id" => id}, _, socket) do
  {:ok,
   socket
   |> assign(:org, AsyncResult.loading())
   |> start_async(:my_task, fn -> fetch_org!(id) end)
end

def handle_async(:my_task, {:ok, fetched_org}, socket) do
  %{org: org} = socket.assigns
  {:noreply, assign(socket, :org, AsyncResult.ok(org, fetched_org))}
end

def handle_async(:my_task, {:exit, reason}, socket) do
  %{org: org} = socket.assigns
  {:noreply, assign(socket, :org, AsyncResult.failed(org, {:exit, reason}))}
end

but I’d like to be able to do

defmodule App.LiveOrg do
  use GenServer

  def start_link(opts) do
    GenServer.start_link(__MODULE__, opts)
  end

  @impl true
  def init(opts) do
    :ok = Phoenix.PubSub.subscribe(App.PubSub, "org:#{opts.id}")
    org = fetch_org!(id)
    :ok = Phoenix.LiveView.async_update(opts.live_view_ref, org)
    {:ok, %{org_id: opts.id, live_view_ref: opts.live_view_ref}}
  end

  @impl true
  def handle_info(_pub_sub_message, state) do
    org = fetch_org!(state.org_id)
    :ok = Phoenix.LiveView.async_update(opts.live_view_ref, org))
    {:noreply, state}
  end
end

# in my live view or live component

def mount(%{"id" => id}, _, socket) do
  {:ok,
   socket
   |> assign(:org, AsyncResult.loading())
   |> start_async(:my_live_org, App.LiveOrg, %{id: id})
end

def handle_async(:my_live_org, {:update, fetched_org}, socket) do
  %{org: org} = socket.assigns
  {:noreply, assign(socket, :org, AsyncResult.ok(org, fetched_org))}
end

def handle_async(:my_live_org, {:exit, reason}, socket) do
  %{org: org} = socket.assigns
  {:noreply, assign(socket, :org, AsyncResult.failed(org, {:exit, reason}))}
end

Just to clarify what’s happening here, I’m asking for start_async to be able to start and supervise arbitrary processes. When it does this it should pass a processes a :live_view_ref option to the process which the new Phoenix.LiveView.async_update/2 function can take to send an async update back to the live view or live component. This allows the started process to send multiple updates, and for those updates to target the underlying phoenix live process and assign correctly (regardless of if that assign is in a live view or a live component).

Conceptually this is nearly identical to the current task based API, and I think we should keep the task based API around as a convenience on top of this API, but I’m a fan of having the full power of OTP rather than just tasks.

Where Next?

Popular in Proposals: Ideas Top

beno
There appears to be no way to escape whitespace in word lists, so sigil_w (and sigil_W) can only be used for lists of single words. Of co...
New
andypearson
Hey all, I have been working on some performance improvements for the Phoenix application I work on. As part of this, through trial and...
New
MUSTDOS
Hello all! assign(socket, :name, “Elixir”) Why can’t we have assign/stream take a group of atoms and maps/structs as a default to r...
New
pelopo
Hi, Oficial docs got this wonderful feature to download the ePub version that we can chuck to our kindles and read it. Now we are in the...
New
Astolfo
When generating a release with phx.gen.release --docker a debian version is used by default to avoid “DNS issues” on Alpine. It seems li...
New
kccarter
This is likely a feature request unless we’re overlooking something, but it would be a nice improvement to the developer experience if th...
New
Matsa59
Lets start by a story, I work with a team on a pretty big CMS with lots of modules. We split the cms using umbrella apps. So we have comp...
New
davydog187
I’ve been enjoying the new Streams API, but I keep needing to work around the lack of access to the underlying items. One issue that cons...
New
dibok
Hi, I’m trying to use phoenix.js in my Qt QML project which has it’s own buildin JavaScript engine. Problem is that (what I googled so f...
New
alaadahmed
Hi folks, I tried Phoenix 1.7 and it is awesome, but I have small suggestion, which in my opinion will organize files in a better way. ...
New

Other popular topics Top

vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
9mm
I am constructing a JSON object (map) and I need to conditionally set a field. I’m trying to write proper elixir-way code… and I’m at a l...
New
lastday4you
I wanted to check elixir version in phoenix because i found that my elixir is 1.5 but when i use Enum.chunk_by it said the function is un...
New
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
dokuzbir
I want to highlight html closing tags when i click a html tag. That works in .html files but doesnt work for html.eex templates. How can...
New
gausby
I asked this very same question on twitter and got some interesting feedback, but I thought it would be a good question to ask here as we...
1207 39297 209
New
dblack
I’ve got an issue with an app and I’ve no idea of how to troubleshoot it. I’m hoping someone here might have seen something similar. I p...
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
PeterCarter
There are pre-rolled solutions for other frameworks that do work. However, Phoenix does not seem to have these. Have people had good expe...
New
hariharasudhan94
Lets say I have map like this fetching from my database %{"_id" => #BSON.ObjectId<58eb1a7a9ad169198c3dXXXX>, "email" => ...
New

We're in Beta

About us Mission Statement