Last active
October 21, 2025 08:53
-
-
Save westmark/7021bc0ede74838616ac937dd07b59a7 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Application.put_env(:sample, Example.Endpoint, | |
| http: [ip: {127, 0, 0, 1}, port: 5001], | |
| server: true, | |
| live_view: [signing_salt: "aaaaaaaa"], | |
| secret_key_base: String.duplicate("a", 64) | |
| ) | |
| Mix.install([ | |
| {:plug_cowboy, "~> 2.5"}, | |
| {:jason, "~> 1.0"}, | |
| {:phoenix, "~> 1.8.1"}, | |
| # please test your issue using the latest version of LV from GitHub! | |
| {:phoenix_live_view, | |
| github: "phoenixframework/phoenix_live_view", branch: "main", override: true} | |
| ]) | |
| # if you're trying to test a specific LV commit, it may be necessary to manually build | |
| # the JS assets. To do this, uncomment the following lines: | |
| # this needs mix and npm available in your path! | |
| # | |
| # path = Phoenix.LiveView.__info__(:compile)[:source] |> Path.dirname() |> Path.join("../") | |
| # System.cmd("mix", ["deps.get"], cd: path, into: IO.binstream()) | |
| # System.cmd("npm", ["install"], cd: Path.join(path, "./assets"), into: IO.binstream()) | |
| # System.cmd("mix", ["assets.build"], cd: path, into: IO.binstream()) | |
| defmodule Example.ErrorView do | |
| def render(template, _), do: Phoenix.Controller.status_message_from_template(template) | |
| end | |
| defmodule Example.ReproLiveComponentWithAsyncResult do | |
| use Phoenix.LiveComponent | |
| def mount(socket) do | |
| {:ok, socket} | |
| end | |
| def render(assigns) do | |
| ~H""" | |
| <div> | |
| <.async_result :let={data} assign={@data}> | |
| <p :for={item <- data} :key={item.id}>{item.value}</p> | |
| </.async_result> | |
| </div> | |
| """ | |
| end | |
| def handle_event(_, _, socket) do | |
| {:noreply, socket} | |
| end | |
| end | |
| defmodule Example.ReproLiveComponent do | |
| use Phoenix.LiveComponent | |
| def mount(socket) do | |
| {:ok, socket} | |
| end | |
| def render(assigns) do | |
| ~H""" | |
| <div> | |
| <p :for={item <- @data} :key={item.id}>{item.value}</p> | |
| </div> | |
| """ | |
| end | |
| def handle_event(_, _, socket) do | |
| {:noreply, socket} | |
| end | |
| end | |
| defmodule Example.HomeLive do | |
| use Phoenix.LiveView, layout: {__MODULE__, :live} | |
| alias Phoenix.LiveView.AsyncResult | |
| def mount(_params, _session, socket) do | |
| {:ok, socket |> assign(:data, AsyncResult.ok([]))} | |
| end | |
| def render("live.html", assigns) do | |
| ~H""" | |
| <script src="/assets/phoenix/phoenix.js"> | |
| </script> | |
| <script src="/assets/phoenix_live_view/phoenix_live_view.js"> | |
| </script> | |
| <%!-- uncomment to use enable tailwind --%> | |
| <%!-- <script src="https://cdn.tailwindcss.com"></script> --%> | |
| <script> | |
| let liveSocket = new window.LiveView.LiveSocket("/live", window.Phoenix.Socket) | |
| liveSocket.connect() | |
| </script> | |
| <style> | |
| * { font-size: 1.1em; } | |
| </style> | |
| {@inner_content} | |
| """ | |
| end | |
| def render(assigns) do | |
| ~H""" | |
| <div class="p-4"> | |
| <p class="my-4"> | |
| Click Load Data. 3 items should be displayed. Then click Remove First entry. The expected result is 2 items displayed. | |
| </p> | |
| <div> | |
| <.async_result :let={data} assign={@data}> | |
| <.live_component module={Example.ReproLiveComponent} id="repro" data={data} /> | |
| </.async_result> | |
| <!-- Uncomment the below to get a different issue | |
| <div style="margin: 10px; height: 1px; background-color: black;"></div> | |
| <.live_component | |
| module={Example.ReproLiveComponentWithAsyncResult} | |
| id="repro_async" | |
| data={@data} | |
| /> | |
| --> | |
| </div> | |
| <div> | |
| <button phx-click="load">Load data</button> | |
| <button phx-click="remove">Remove first entry</button> | |
| </div> | |
| </div> | |
| """ | |
| end | |
| def handle_event("load", _, socket) do | |
| socket = | |
| assign_async(socket, :data, fn -> | |
| Process.sleep(100) | |
| {:ok, | |
| %{data: [%{id: 1, value: "First"}, %{id: 2, value: "Second"}, %{id: 3, value: "Third"}]}} | |
| end) | |
| {:noreply, socket} | |
| end | |
| def handle_event("remove", _, socket) do | |
| socket = | |
| assign_async(socket, :data, fn -> | |
| Process.sleep(100) | |
| {:ok, %{data: [%{id: 2, value: "Second"}, %{id: 3, value: "Third"}]}} | |
| end) | |
| {:noreply, socket} | |
| end | |
| end | |
| defmodule Example.Router do | |
| use Phoenix.Router | |
| import Phoenix.LiveView.Router | |
| pipeline :browser do | |
| plug(:accepts, ["html"]) | |
| end | |
| scope "/", Example do | |
| pipe_through(:browser) | |
| live("/", HomeLive, :index) | |
| end | |
| end | |
| defmodule Example.Endpoint do | |
| use Phoenix.Endpoint, otp_app: :sample | |
| socket("/live", Phoenix.LiveView.Socket) | |
| plug Plug.Static, from: {:phoenix, "priv/static"}, at: "/assets/phoenix" | |
| plug Plug.Static, from: {:phoenix_live_view, "priv/static"}, at: "/assets/phoenix_live_view" | |
| plug(Example.Router) | |
| end | |
| {:ok, _} = Supervisor.start_link([Example.Endpoint], strategy: :one_for_one) | |
| Process.sleep(:infinity) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment