shankardevy

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

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

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.

16
Post #2
benwilson512

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

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.

12
Post #4
LostKobrakai

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.

Where Next?

Popular in Discussions Top

Jayshua
I recently came across the javascript library htmx. It reminded me a lot of liveview so I thought the community here might be interested....
New
owaisqayum
I have a sample string sentence = "Hello, world ... 123 *** ^%&*())^% %%:>" From this string, I want to only keep the integers, ...
New
thojanssens1
It would be nice to be able to define a redirect from one route to another from the router.ex file. E.g.: redirect "/", UserController, ...
New
AngeloChecked
What learn first? Rust or Elixir Hi Elixir community! I’m here because i want learn a new language. I’m a junior developer and mainly i ...
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 39523 209
New
AlexMcConnell
The reason that Rails is as popular as it is is because it’s very easy for relatively inexperienced developers to get a lot of work done....
588 19675 166
New
New
chulkilee
Here are the list of HTTP client libraries/wrappers, and some thoughts on HTTP client in general. I’d like to hear from others how they w...
New
pdgonzalez872
If this has been asked here before, please point me to where it was asked as I didn’t find it when I searched the forum. Maybe a mailing ...
New
Qqwy
Looking at the stacks that existing large companies have used, WhatsApp internally uses Mnesia to store the messages, while Discord uses ...
New

Other popular topics Top

axelson
This post is a wiki (feel free to hit the edit button near the bottom right of this post to add your own changes!) This post collects co...
239 48475 226
New
Darmani72
If I have a post route which an argument: post /my_post_route/:my_param1, MyController.my_post_handler How would get the post params ...
New
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
AngeloChecked
What learn first? Rust or Elixir Hi Elixir community! I’m here because i want learn a new language. I’m a junior developer and mainly i ...
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New
Qqwy
Update: How to use the Blogs & Podcasts section You can post links to your blog posts or podcasts either in one of the Official Blog...
3271 127536 1222
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
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New

We're in Beta

About us Mission Statement