Phoenix Form from Dynamic List

My application allows users to upload a sheet file (xlsx) and I want to allow users to choose which columns (header) they really want to import, and they can also change column name.

Example:

File Header: name, surname, email

Header list to be choose: [{0, “name”, true}, {1, “surname”, true}, {2, “email”, true}]

How can I create a phoenix form that accepts dynamic number of inputs (per column) ?
Their files can contain at least 1 column to several columns (dozens).

Current Controller:

...
        conn
        |> put_flash(:info, gettext("File uploaded successfully."))
        |> render("header.html", id: id, header: select_header)
...

Expected template:

<%= form_for @conn, people_list_path(@conn, :header_create, @locale, @id), [method: :post, as: :header], fn f -> %>

  <div class="form-group">
    <%= label f, gettext("Index"), class: "control-label" %>
    <%= text_input f, :i0, placeholder: gettext("Index"), class: "form-control" %>
    <%= error_tag f, :i0 %>
  </div>

  <div class="form-group">
    <%= label f, gettext("Name"), class: "control-label" %>
    <%= text_input f, :i1, placeholder: gettext("Name"), class: "form-control" %>
    <%= error_tag f, :i1 %>
  </div>

  <div class="form-group">
    <%= label f, gettext("Import"), class: "control-label" %>
    <%= checkbox f, :i2, placeholder: gettext("Import"), class: "form-control" %>
    <%= error_tag f, :i2 %>
  </div>

  <div class="form-group">
    <div class='control-group'>
      <%= submit gettext("Submit"), class: "btn btn-primary" %>
    </div>
  </div>

<% end %>

You can do a few various things, like using a list of dynamic field names, making the html manually, etc…

What code have you tried that has not worked?

It needs improvement in UI (Anyone knows how to improve Phoenix.Tags? ), but it’s working.

Header

[{0, "name", true}, {1, "surname", true}, {2, "email", true} ...]

Form

<%= form_for @conn, people_list_path(@conn, :header_create, @locale, @id), [method: :post, as: :header], fn f -> %>

  <%= hidden_input f, :path, value: @local_file %>

  <%= Enum.map(@header, fn x -> PhoenixHelper.tuple_to_form(x, :f) end) %>

  <div class="form-group">
    <div class='control-group'>
      <%= submit gettext("Submit"), class: "btn btn-primary" %>
    </div>
  </div>

<% end %>

Helper

  def tuple_to_form({a, b, c} = tuple, form) do
    n = Integer.to_string(a)
    t_a = n <> "_0" |> String.to_atom
    t_b = n <> "_1" |> String.to_atom
    t_c = n <> "_2" |> String.to_atom
    f_a = Phoenix.HTML.Form.hidden_input(form, t_a, value: a)
    f_b = Phoenix.HTML.Form.text_input(form, t_b, value: b, class: "form-control")
    f_c = Phoenix.HTML.Form.checkbox(form, t_c, value: c, class: "form-control")
    div_begin = Phoenix.HTML.Tag.tag(:div, class: "form-group")
    div_end = Phoenix.HTML.Tag.tag(:div)
    br = Phoenix.HTML.Tag.tag(:br)
    [f_a, div_begin, f_b, f_c, div_end, br]
  end

You can use an embedded eex template in that instead. :slight_smile:

  def tuple_to_form({a, b, c} = tuple, form) do
    n = Integer.to_string(a)
    t_a = n <> "_0" |> String.to_atom
    t_b = n <> "_1" |> String.to_atom
    t_c = n <> "_2" |> String.to_atom
    ~E"""
    <%= Phoenix.HTML.Form.hidden_input(form, t_a, value: a) %>
    <p class = "form-group">
      <%= Phoenix.HTML.Form.text_input(form, t_b, value: b, class: "form-control") %>
      <%= Phoenix.HTML.Form.checkbox(form, t_c, value: c, class: "form-control") %>
    </p>
    """
  end

And I’m getting less surprised at how poorly syntax highlighting works here. ^.^;

It worked with “import Phoenix.HTML”.

Thanks.

1 Like

Ah right, that comes by default in any View module and I thought you had that in a View module. ^.^;