Phoenix Live View Unable to insert with associations.

Hello all,
I am getting KeyError for current_user in the liveview even though my socket contains current_user. Please go through below:

I have generated live view with this command:

mix phx.gen.live RegistrationSteps.Details UserDetail user_details user_id:references:users first_name last_name company:unique

So i did modify user_details by removing field of user_id and added association like this in the schema:

schema "user_details" do
  field :first_name, :string
  field :last_name, :string
  field :company, :string

  belongs_to :user, Accounts.User, type: :binary_id

  timestamps(type: :utc_datetime)
end

In the changeset i added association constraint :

def changeset(user_detail, attrs) do
  user_detail
  |> cast(attrs, [:user_id,:first_name, :last_name, :company])
  |> validate_required([:user_id, :first_name, :last_name, :company])
  |> unique_constraint(:company)
  |> assoc_constraint(:user)
end

For user schema i have added this has_one relation:

has_one :user_details, Details.UserDetail

In the context for creating user, i have added extra attribute like:

def create_user_detail(user, attrs \\ %{}) do
  user
  |> Ecto.build_assoc(:user_details)
  |> UserDetail.changeset(attrs)
  |> Repo.insert()
end

For live_session i have already setup on_mount function in the router inorder to fetch current_user from the socket assigns.

live_session :registration_session, on_mount: [{LngxWeb.Auth.UserAuth, :mount_current_user}]  do
  ....
end

From the generated live view index.ex:

def handle_params(params, _url, socket) do
  {:noreply, apply_action(socket, socket.assigns.live_action, params)}
end

defp apply_action(socket, :new, _params) do
  # Logger.warning("Current Socket Connection: #{inspect(socket.assigns)}")

  socket
  |> assign(:page_title, "New User detail")
  |> assign(:user_detail, %UserDetail{})
end

The part of index.html.heex Shows like this:

<.modal
  :if={@live_action in [:new, :edit]}
  id="user_detail-modal"
  show
  on_cancel={JS.patch(~p"/registration/details")}
>
  <.live_component
    module={MyWeb.UserDetailLive.FormComponent}
    id={@user_detail.id || :new}
    title={@page_title}
    action={@live_action}
    user_detail={@user_detail}
    patch={~p"/registration/details"}
  />
</.modal>

For the handle_event save in form_component, i tried to pass the current_user parameter from the socket assigns.

def handle_event(
      "save",
      %{"user_detail" => user_detail_params},
      socket
    ) do
  current_user = socket.assigns.current_user
  save_user_detail(socket, socket.assigns.action, user_detail_params, current_user)
end

So When i click on save i am getting 2 errors:

  1. KeyError current_user not found:
  2. user_id cannot be null
**(KeyError) key :current_user not found in:** %{
  id: :new,
  title: "New User detail",
  form: %Phoenix.HTML.Form{
    source: #Ecto.Changeset<
      action: :validate,
      changes: %{first_name: "bad", last_name: "bad", company: "bad"},
      errors: [user_id: {"can't be blank", [validation: :required]}],
      data: #Lngx.RegistrationSteps.Details.UserDetail<>,
      valid?: false,
      ...

I made sure that socket.assigns contains current_user.
Beginner in elixir and phoenix, help and some resources on building live views with table associations will be much appreciated. Thank you.

(I reformatted your post to use triple-backticks (```) for code instead of > quotes)

Some questions/comments:

  • what line is the error happening on? My suspicion is it’s the current_user = socket.assigns.current_user line in handle_event.
  • the user_id can't be blank message is likely a red herring, because that’s what you’d get with the empty %UserDetail{} that apply_action is assigning
  • depending on the location of the error, it may be useful to see the source of save_user_detail

Thank You for refactoring.

  • Error Happening: Yes the error is happening on:current_user = socket.assigns.current_user line in handle_event. The socket in the handle event does not contain current_user. however it does contain in apply_action function.
    Here is the source of save_user_details, Since i thought i have Ecto build association, user_id can be passed automatically.
defp save_user_detail(socket, :new, user_detail_params, current_user) do
    case Details.create_user_detail(current_user, user_detail_params) do
      {:ok, user_detail} ->
        notify_parent({:saved, user_detail})

        {:noreply,
         socket
         |> put_flash(:info, "User detail created successfully")
         |> push_patch(to: socket.assigns.patch)}

      {:error, %Ecto.Changeset{} = changeset} ->
        {:noreply, assign(socket, form: to_form(changeset))}
    end
  end

I also tried to re assign current user from the apply_action as this sockets contain current_user:

defp apply_action(socket, :new, _params) do
    # Logger.warning("Current Socket Connection: #{inspect(socket.assigns.current_user)}")

    socket
    |> assign(:page_title, "New User detail")
    |> assign(:user_detail, %UserDetail{})
    |> assign(:current_user, socket.assigns.current_user)
  end

Still i am getting the same KeyError at: current_user = socket.assigns.current_user