nallwhy

nallwhy

What is the best way to handle file uploads in a LiveView dynamic form?

In my application, users can upload multiple Contracts as files for a User from a form.

defmodule User do
  use Ecto.Schema
  import Ecto.Changeset

  @primary_key false
  embedded_schema do
    field :name, :string

    embeds_many :contracts, Contract, primary_key: false, on_replace: :delete do
      field :file_url, :string
    end
  end

  def changeset(%__MODULE__{} = struct, attrs) do
    struct
    |> cast(attrs, [:name])
    |> cast_embed(
      :contracts,
      with: &changeset_contract/2,
      sort_param: :contracts_sort,
      drop_param: :contracts_drop
    )
  end

  def changeset_contract(%__MODULE__.Contract{} = struct, attrs) do
    struct
    |> cast(attrs, [:file_url])
  end
end

I want to handle this using the Dynamic Form feature of LiveView.

defmodule MyAppWeb.Live do
  ...
  @impl true
  def render(assigns) do
    ~H"""
    <.simple_form for={@user_form} phx-change="validate" phx-submit="submit">
      <.input label="name" field={@user_form[:name]} />
      <.inputs_for :let={fc} field={@user_form[:contracts]}>
        <input type="hidden" name="user[contracts_sort][]" value={fc.index} />

        <.live_file_input upload={???} /> # <-- this is the problem

        <.label>
          <input type="checkbox" name="user[contracts_drop][]" value={fc.index} class="hidden" />
          <.icon name="hero-x-mark" class="w-6 h-6 relative top-2" /> remove
        </.label>
      </.inputs_for>

      <label class="block cursor-pointer">
        <input type="checkbox" name="user[contracts_sort][]" class="hidden" />
        <.icon name="hero-plus-circle" class="mr-1 align-text-bottom" />add more
      </label>

      <:actions>
        <.button>Submit</.button>
      </:actions>
    </.simple_form>
  end
end

In the validation event, it’s possible to dynamically add file uploads using allow_upload/3 . However, due to Contracts being dynamically created and deleted, the index keeps changing (what was originally the 3rd index becomes the 2nd index if the 1st index is deleted), making it difficult to track. I would like to use the _persistent_id, which does not change, but it’s hard to track since _persistent_id is unknown at the point when the input is added.

What is the best way to handle file uploads in a LiveView dynamic form?

First Post!

codeanpeace

codeanpeace

Have you tried using the :ref field on the UploadEntry struct?

<%!-- render each avatar entry --%>
 <%= for entry <- @uploads.avatar.entries do %>
    ...
    <%!-- a regular click event whose handler will invoke Phoenix.LiveView.cancel_upload/3 --%>
    <button type="button" phx-click="cancel-upload" phx-value-ref={entry.ref} aria-label="cancel">&times;</button>

Cancel an entry

Upload entries may also be canceled, either programmatically or as a result of a user action. For instance, to handle the click event in the template above, you could do the following:

@impl Phoenix.LiveView
def handle_event("cancel-upload", %{"ref" => ref}, socket) do
  {:noreply, cancel_upload(socket, :avatar, ref)}
end

source: LiveView Uploads guide

Where Next?

Popular in Questions Top

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
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
chrisalley
ExUnit now has describe blocks which is a welcome addition coming from RSpec. In the docs, it states that nested hierarchies of describe ...
New
mgjohns61585
Could someone help me? I’m making my first elixir program, number guessing game. I can’t figure out how to convert the user’s guess from ...
New
JulienCorb
I am trying to implement my new.html.eex file to create new posts on my website. new.html.eex: &lt;h1&gt;Create Post&lt;/h1&gt; &lt;%= ...
New
SoCreat
i’m a new one to elixir which editor can i use vs code? or atom? Thanks! :smiley:
New
RisingFromAshes
I’ve read in another post that it may be possible with a router helper - but I couldn’t find an appropriate one, and tbh, I’m still just ...
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
earth10
Hi, I’m just starting to build a side-project with Elixir and Phoenix and doing some basic test with Elixir alone. What strikes me is th...
New

Other popular topics Top

New
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
pmjoe
I have a relationship of love and hate with Elixir. Lots of things are just absolutely right, but there are some things that are kind of ...
New
msaraiva
Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative...
564 43757 214
New
belgoros
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers’ Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
JakeBecker
TL;DR: I’ve just released an implementation of Microsoft’s IDE-independent Language Server Protocol for Elixir. It adds language support ...
1144 54120 245
New
dogweather
I wrote this comment on r/haskell, and it’s not popular there. :wink: But I think I’m on to something… Haskell reminds me of Java, and e...
New
svb
Hi! Currently I want to submit a form by pressing the Enter key. However, since my input field is of type “textarea” this is just adds a...
New

We're in Beta

About us Mission Statement