when inspecting session within a liveView mount, it does not include a user inserted through an authplug. The authplug however seems to be working fine when using normal (non-liveView) controllers. i.e. user is available in conn.
session should include "current_user" value as with normal controller. Everything else works well with liveView.
defmodule PlayaroundWeb.Router do
use PlayaroundWeb, :router
pipeline :browser do
plug :accepts, ["html"]
plug :fetch_session
plug Phoenix.LiveView.Flash
plug :protect_from_forgery
plug :put_secure_browser_headers
plug PlayaroundWeb.AssignUser
end
pipeline :api do
plug :accepts, ["json"]
end
scope "/", PlayaroundWeb do
pipe_through :browser
get "/", PageController, :index
live "/people", PersonLive
end
# Other scopes may use custom stacks.
# scope "/api", PlayaroundWeb do
# pipe_through :api
# end
end
defmodule PlayaroundWeb.PersonLive do
use Phoenix.LiveView
alias PlayaroundWeb.PersonView
alias Playaround.Entities.Person
alias Playaround.Entities
def render(assigns) do
Phoenix.View.render(PlayaroundWeb.PersonView, "index.html", assigns)
end
def mount(params, session, socket) do
IO.puts "PARAMS ------------------------------"
IO.inspect params
IO.puts "Session -----------------------------"
IO.inspect session
IO.puts "SOCKET -------------------------------"
IO.inspect socket
{:ok, socket}
end
def handle_params(_params, _url, socket) do
{:noreply, socket |> fetch()}
end
defp fetch(socket) do
assign(socket,
people: Entities.list_people(),
changeset: Entities.change_person(%Person{})
)
end
def handle_event("create", %{"person" => person_params}, socket) do
case Entities.create_person(person_params) do
{:ok, person} ->
{:stop,
socket
|> put_flash(:info, "Person created successfully")}
{:error, changeset} ->
{:noreply, assign(socket, changeset: changeset)}
end
end
end
defmodule PlayaroundWeb.AssignUser do
import Plug.Conn
def init(opts), do: opts
def call(conn, params) do
assign(conn, :current_user, "SIMPLE_TEST_USER")
end
end
[info] CONNECTED TO Phoenix.LiveView.Socket in 0┬Ás
Transport: :websocket
Serializer: Phoenix.Socket.V2.JSONSerializer
Connect Info: %{session: %{"_csrf_token" => "wnoGFcDuZ1s06ZmIrf18PtWa"}}
Parameters: %{"_csrf_token" => "Lhw6LAgbIgYuXCZWBxsceihVQAwoPjwVYrUkNxfstmUf1Aq3Z3q4xJkt", "vsn" => "2.0.0"}
PARAMS ------------------------------
%{}
Session ------------------------
%{"_csrf_token" => "wnoGFcDuZ1s06ZmIrf18PtWa"}
SOCKET
#Phoenix.LiveView.Socket<
assigns: %{
flash: %{},
live_view_action: nil,
live_view_module: PlayaroundWeb.PersonLive
},
changed: %{},
endpoint: PlayaroundWeb.Endpoint,
id: "phx-FfM21Ob1oUDHrwSB",
parent_pid: nil,
view: PlayaroundWeb.PersonLive,
...
>
Assigns are not passed from Plug to LiveView, only whatever is in the session. If you can provide a minimal web application that reproduces the error, then we can take a look at it. There is probably a misconiguration somewhere. :) Thanks!
@josevalim
Makes sense. Thank you sir. I am however still stuck with how to transfer the user from conn to the live view if assigns are not transfered (in which case using the session: %{ "user" => user } approach would be the only option. I am using Pow (on the main app). The playaround app can be found below (when you get a chance). highly appreciated.
Everything in the session will automatically be availble on the server.
So if instead of:
https://github.com/knitero/liveview/blob/master/lib/playaround_web/plugs/assign_user.ex#L9
You do:
put_session(conn, "current_user", "Ronzie")
You will see the current user information available on the LiveView.
Oh wow! Perfect! That worked. Thank you so much!
You can use assign_new and fallback to fetching the user from the session, which allows you to avoid the extra lookup on the initial HTTP request, for example:
def mount(_parmas, %{"user_id" => user_id}, socket) do
{:ok, assign_new(socket, :current_user, fn -> Accounts.get_user!(user_id) end)}
end
Works great. Thanks
Thank you for the helpful example and code snippet!
def mount(_parmas, %{"user_id" => user_id}, socket) do {:ok, assign_new(socket, :current_user, fn -> Accounts.get_user!(user_id) end)} end
For the use case where all of our live views use @current_user, does some shorthand or macro exist?
ie) Assume that the user_id is set in the session from an existing plug. Following the Live Layouts guide, if live.html.leex were:
<%= if @current_user do %>
<h3><%= @current_user.username %> (<%= @current_user.id %>)</h3>
<% end %>
<%= @live_view_module.render(assigns) %>
and we applied the following macro on all of our LiveViews:
# lib/myapp_web.ex
# ...
def live do
quote do
use Phoenix.LiveView, layout: {MyAppWeb.LayoutView, "live.html"}
alias MyAppWeb.Router.Helpers, as: Routes
end
end
I want to simplify all of my live views:
# lib/myapp_web/live/clock_live.ex
defmodule MyAppWeb.ClockLive do
use MyAppWeb, :live
alias MyApp.Accounts
def mount (_params, %{"user_id" => user_id}, socket) do
if connected?(socket), do: :timer.send_interval(1000, self(), :tick)
{:ok, socket
# how to dedupe this line from all of my live views?
|> assign_new(:current_user, fn -> Accounts.get_user!(user_id) end)
|> put_date()}
end
# ...
end
It would be useful to have a standard way of setting assigns for all live views.
Best option today is to make a mode, say MyAppWeb.LiveHelpers and make a prepare_assigns/2 function which accepts the socket and session, and each LV calls it in mount.
Most helpful comment
You can use
assign_newand fallback to fetching the user from the session, which allows you to avoid the extra lookup on the initial HTTP request, for example: