6
votes

I'm trying to populate a field :params (from a Model/Schema) which is a map. I've got a working form_for and I'd like to populate this :params map through checkboxes so that when the form is submitted the controller will receive something like %{... params => %{"param1" => "true", "param2" => "false"}} ...

I've looked at inputs_for, but that doesn't seem to do what I need since it relies on nested schemas and models, and that would mean I need to create a new schema for each new set of parameters (I need something generic that doesn't need changes to the source code if the parameters change).

<%= form_for @changeset, audit_audit_path(@conn, :new_tool_request, @audit.id),
       fn f -> %>

  <%= render LayoutView, "changeset_error.html", conn: @conn, changeset: @changeset, f: f %>

  <div class="form-group"><label>Tool</label>
    <%= select f, :toolname, tools %>
  </div>
  <div class="form-group"><label>Parameter 1</label>
    <%= checkbox f, :param1 %>
  </div>
  <div class="form-group"><label>Parameter 2</label>
    <%= checkbox f, :param2 %>
  </div>

  <div class="form-group"><label>Date of Execution</label>
    <%= datetime_select f, :date %>
  </div>
  <div class="form-group">
    <%= hidden_input f, :audit_id, value: @audit.id %>
  </div>

  <%= submit "Request", class: "btn btn-primary" %>
<% end %>

So instead of having those checkboxes for param1 and param2, I need to get all those parameters into a map. If another form is rendered with different checkboxes for the parameters, it must populate without having any relationship to a schema.

Thanks!

2
If I got your question right, I think you just need to generate a name attribute with a namespace "params" for them, so something like <%= checkbox f, :param1, name: "params[param1]" %> and <%= checkbox f, :param2, name: "params[param2]" %> but I believe with form_for it should be done automatically.NoDisplayName
That wasn't exactly what I wanted but it works! Thank you. Just for reference, if you do what @JustMichael suggested, you will submit another form/map, in this case with the name "params". In your controller just define a function that receives the params like def action_to_use(conn, "form" => form, "params" => params and you will get the second form you created with the namespace.Sasha Fonseca

2 Answers

3
votes

In fact, I think, if in situation like that:

  schema "checkmapss" do
    field :name, :string
    field :options, :map

    timestamps()
  end

We just need to do is in form.html.eex:

  <div class="form-group">
    <%= label f, :options, class: "control-label" %>
    <%= text_input f, :options_one, name: "checkmap[options][one]", class: "form-control" %>
    <%= error_tag f, :options %>
  </div>
  <div class="form-group">
    <%= label f, :options, class: "control-label" %>
    <%= text_input f, :options_two, name: "checkmap[options][two]", class: "form-control" %>
    <%= error_tag f, :options %>
  </div>

Then the changeset function will help us finish other thing.

0
votes

I came across the same problem but with simple HTML forms and elixir, ecto pattern matching got it to work nicely and easily.

Ecto Schema definition

schema "stuff" do
  field :name, :string
  field :settings, :map
end

Controller or Resource :edit action

changeset = StuffContext.change_stuff(stuff)

In your template / form

<%= form_for @changeset, Routes.stuff_path(@conn, :save, @stuff), fn f -> %>
   ...
   <%= label f, :currency %>
   <%= text_input f, :stuff_currency, name: "stuff[settings][currency]", 
   value: @stuff.settings["currency"], required: true %>
   <%= error_tag f, :settings %> <-- Important errors come from ecto validation logic we put into changeset, not the made up currency
   ...

Then in your controller :save action

def save(conn, %{"id" => id, "stuff" => stuff_params}) do
  stuff = StuffContext.get(id)
  ...
  IO.inspect(stuff_params) ->

  %{"name" => "welcome", "settings" => %{"currency" => "USD"}}
  ...
  StuffContext.update_stuff(stuff, stuff_params)