Phoenix LiveView Async Assigns

From ElixirBlocks
Jump to: navigation, search

Example

defmodule AppWeb.PageLive do
  use AppWeb, :live_view

  alias Phoenix.LiveView.AsyncResult
  @impl true
  def mount(_params, _session, socket) do
    {:ok,
     assign_async(socket, :number, fn -> {:ok, %{number: really_complicated_function()}} end)}
  end

  @impl true
  def handle_event("generate_number", _, socket) do
    {:noreply,
     socket
     |> assign(:number, AsyncResult.loading())
     |> start_async(:get_random_number, fn -> really_complicated_function() end)}
  end

  @impl true 
  def handle_async(:get_random_number, {:ok, res}, socket) do
    %{number: number} = socket.assigns
    {:noreply, assign(socket, :number, AsyncResult.ok(number, res))}
  end

  def handle_async(:get_random_number, {:exit, _reason}, socket) do
    %{number: number} = socket.assigns

    {:noreply,
     assign(socket, :number, AsyncResult.failed(number, "Failed to generate a number."))}
  end

  @impl true
  def render(assigns) do
    ~H"""
    <div class="flex justify-center items-center h-screen bg-gradient-to-r from-blue-500 to-purple-600">
      <div class="text-center bg-white shadow-lg rounded-lg p-10">
        <h1 class="text-4xl font-bold text-gray-800 mb-5">Random Number Generator</h1>
        <!-- Display Area -->
        <.async_result :let={number} assign={@number}>
          <:loading>
            <div
              class="spinner-border animate-spin inline-block w-8 h-8 border-4 rounded-full text-blue-700"
              role="status"
            >
              <span class="visually-hidden">Loading...</span>
            </div>
          </:loading>
          <:failed :let={_reason}>
            <div class="mt-5 text-red-500">
              Failed to generate a number.
            </div>
          </:failed>

        <div  class="text-6xl font-bold text-green-600 mb-5">
          <%= number %>
        </div>
          <button
            phx-click="generate_number"
            class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded transition duration-300 ease-in-out transform hover:scale-105"
          >
            Generate Random Number
          </button>
        </.async_result>
      </div>
    </div>
    """
  end

  def really_complicated_function() do
    # Simulate a really complicated function, takes a long time to run and will fail sometimes
    :timer.sleep(1000)

    if Enum.random(1..10) > 5 do
      :error
    else
      Enum.random(1..100)
    end
  end
end