shankardevy
LiveView: Merge assigns from conn to socket
Currently when I assign a value in conn.assigns in the router, the assigned value is not merged with the socket.assigns.
Patching this line https://github.com/phoenixframework/phoenix_live_view/blob/v0.11.1/lib/phoenix_live_view/static.ex#L114 to include the assigns from conn works.
However, I do not understand if there is any reason for not doing this. Also I see the conn.assigns is copied to private.assign_new but don’t understand the purpose.
Anyone having background on this could explain the rationale?
Most Liked
benwilson512
Live view renders happen twice. There is the first static render that happens as part of an HTTP GET request. The conn is evaluated, a socket built / mounted, the page rendered. After that though the socket goes away entirely, it was just there to address the HTTP get request, there is no persistent connection.
Then the javascript on the page runs, connects via websocket, and does a second render. This time the socket sticks around because you’ve got a persistent connection. Notably though on this run, there is no @conn. In fact it’s a totally separate request, that could even happen to a totally different server from the GET request.
In the GET request, the assigns can be copied into the socket, because the socket is just basically exists to help you avoid writing totally different code for the static render. BUT on the second request, there’s no @conn for the values to be copied from at all. This is why the socket |> assign_new function takes an anonymous function arg. On the GET request assign_new will just use the assign from the @conn. But on the second live request, how can it get its value? It runs the anonymous function.
benwilson512
This is basically correct. What I did in my app is create a %Context{} struct which holds things like the current user. Then I setup this context for both plug and in my live view:
In my plug:
case Common.Sensetra.lookup_context(conn.host, org_id, token) do
{:error, :no_grant} ->
conn
|> redirect(to: "/")
|> halt
{:ok, ctx} ->
conn
|> assign(:context, ctx)
|> assign(:host, conn.host)
|> merge_assigns(assigns_from_context(ctx))
{:error, %{"errors" => _}} ->
conn
|> put_session(:token, nil)
|> redirect(to: "/")
|> halt()
end
In my live view:
# elsewhere
import CommonWeb.Socket
# then
def mount(params, session, socket) do
socket = init_assigns(params, session, socket)
defmodule CommonWeb.Socket do
import Phoenix.LiveView
@doc """
This initializes the socket assigns
"""
def init_assigns(params, session, %Phoenix.LiveView.Socket{} = socket) do
%{
"token" => token
} = session
socket
|> assign_new(:token, fn -> token end)
|> assign_new(:host, fn ->
get_connect_params(socket)["host"] || raise "pass in the host!"
end)
|> setup_context(session, params)
end
defp setup_context(socket, _session, params) do
socket =
socket
|> assign_new(:context, fn ->
{:ok, ctx} =
Common.Sensetra.lookup_context(
socket.assigns.host,
params["organization_id"],
socket.assigns.token
)
ctx
end)
socket
|> assign(CommonWeb.Plug.Application.assigns_from_context(socket.assigns.context))
end
end
I use assign_new because I also call Common.Sensetra.lookup_context() inside of a plug. That plug will do things like redirect you to login if there is no current user. Since I look it up in the conn, I use assign_new so that in the socket part of the GET request it isn’t called twice.
You can mostly ignore the whole "host" thing I pass in. Our application does subdomain based theming so it’s important for us.
LostKobrakai
If you’re in the static render and you do assign_new(socket, :user, fn -> fetch_user() end) then phoenix will look at conn.assigns.user first and copy that value before falling back to executing the anonymous function.
Popular in Discussions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #hex









