LiveBulkAsync is a small library that extends LiveView's async support to work with LiveComponent's update_many
function.
First add LiveBulkAsync
to your list of dependencies in mix.exs
:
def deps do
[
{:live_bulk_async, "~> 0.1.0"}
]
end
Now you can directly add support for it in a specific component:
defmodule MyComponent do
use Phoenix.LiveComponent
# Add this
import LiveBulkAsync
...
end
Or you can enable it in all your components by adding it to your Web
module live_component
function:
def live_component do
quote do
...
# Add this
import LiveBulkAsync
end
end
Now inside your component update_many
function, you can use it like this for start_many_async
:
defmodule MyComponent do
@moduledoc false
use Phoenix.LiveComponent
def update_many(assigns_and_sockets) do
assigns_and_sockets
|> Enum.map(fn {assigns, socket} ->
socket |> assign(assigns) |> assign(loading?: true)
end)
|> start_many_async(:content, &load/0)
end
def handle_async(:content, {:ok, content}, socket) do
{:noreply, assign(socket, loading?: false, content: content)}
end
def handle_async(:content, {:exit, reason}, socket) do
{:noreply, socket}
end
def render(assigns) do
~H"""
<div id={@id}>
<div :if={@loading?}>loading...</div>
<div :if={not @loading?}>{@content}</div>
</div>
"""
end
defp load! do
# Load something here
["content 1", "content 2"]
end
end
And like this for assing_many_async
:
defmodule MyComponent do
@moduledoc false
use Phoenix.LiveComponent
def update_many(assigns_and_sockets) do
assigns_and_sockets
|> Enum.map(fn {assigns, socket} ->
socket |> assign(assigns) |> assign(loading?: true)
end)
|> assign_many_async(:content, fn -> load!() end)
end
def render(assigns) do
~H"""
<div id={@id}>
<.async_result :let={content} assign={@content}>
<:loading>Loading...</:loading>
<:failed :let={reason}><pre>{inspect(reason)}</pre></:failed>
<div>{content}</div>
</.async_result>
</div>
"""
end
defp load! do
# Load something here
{:ok, %{content: ["content 1", "content 2"]}}
end
end
You can also cancel an already running task using cancel_many_async
, just keep in mind that calling it will cancel the task for all the components that are using it, not only the one you called it from.
You can cancel a task directly from the update_many
call like this:
def update_many(assigns_and_sockets) do
assigns_and_sockets
|> Enum.map(fn {assigns, socket} -> socket end)
|> cancel_many_async(:content)
end
Or from any other callback inside the component that has access to the socket:
def handle_event("cancel_load", _params, socket) do
socket = cancel_many_async(socket, :content)
{:noreply, socket}
end