0
votes

I've got this database schema for a new user.

defmodule XYZ.User do
  use XYZ.Web, :model

  schema "users" do
    field :lawyer, :boolean
    field :firm, :boolean
    field :first_name, :string
    field :last_name, :string
    field :phone, :string
    field :email, :string

    timestamps
  end

  def changeset(model, params \\ :empty) do
    model
    |> cast(params, [:lawyer, :firm, :first_name, :last_name, :phone, :email])
    |> validate_required([:first_name, :last_name, :phone, :email])
  end

end

The UserController handles /users/new by creating a new User changeset and passing it to a web view/template for rendering.

defmodule XYZ.UserController do
  use XYZ.Web, :controller

  def new(conn, _params) do
    changeset = User.changeset(%User{})
    render conn, "new.html", changeset: changeset
  end

The new.html.eex template uses a Phoenix.HTML helper function to render the lawyer/firm as two radio buttons.

<%= form_for @changeset, user_path(@conn, :create), fn f -> %>
  <div class="form-group">
    <%= radio_button f, :lawyer, "true", checked: "checked %>
    <%= label f, :user_lawyer, "Lawyer" %>
    <%= radio_button f, :firm, "true" %>
    <%= label f, :user_firm, "Firm" %>
  </div> 
  ...

The rendered HTML for the source code above is as follows.

div class="form-group">
  <input checked="checked" id="user_lawyer_true" name="user[lawyer]"  type="radio" value="true">
  <label for="user_user_lawyer">Lawyer</label>
  <input id="user_firm_true" name="user[firm]" type="radio" value="true">
  <label for="user_user_firm">Firm</label>
div>

With the code above submitting the form will save the value of true to the the column in the database record that corresponds to the radio button that was selected on the form.

However, the two radio buttons don't function as a group. It is possible to select both radio buttons when only one should be selected.

Changing the Phoenix.HTML helper functions, adding name: "type" to the options, will group the two radio buttons, but the true value of the selected radio button is not persisted to the database when the form is submitted.

Here's the modified helper functions.

<%= form_for @changeset, user_path(@conn, :create), fn f -> %>
  <div class="form-group">
    <%= radio_button f, :lawyer, "true",  name: "type", checked: "checked" %>
    <%= label f, :user_lawyer, "Lawyer" %>
    <%= radio_button f, :firm, "true", name: "type" %>
    <%= label f, :user_firm, "Firm" %>
  </div>
  ...

And here's the rendered HTML for the above source code.

div class="form-group">
  <input checked="checked" id="user_lawyer_true" name="type" type="radio" value="true">
  <label for="user_user_lawyer">Lawyer</label>
  <input id="user_firm_true" name="type" type="radio" value="true">
  <label for="user_user_firm">Firm</label>
/div>

How can I make the two radio buttons behave as a group while still persisting the correct values to the database?

I'm using the following versions of software:

  • Elixir v1.3.4
  • Phoenix v1.2.1
  • PostgreSQL v9.5.4

Thanks in advance.

----- SOLUTION -----

I ended up implementing one of @cpjolicoeur suggestions, modifying the User schema, removing the boolean lawyer and firm fields and adding a single string field called type.

schema "users" do
  field :type, :string
  field :first_name, :string
  field :last_name, :string
  field :phone, :string
  field :email, :string

  timestamps
end

I then modified the new.html.eex template:

<%= form_for @changeset, user_path(@conn, :create), fn f -> %>
  <div class="form-group">
    <%= radio_button f, :type, "lawyer", checked: "checked" %>
    <%= label f, :user_lawyer, "Lawyer" %>
    <%= radio_button f, :type, "firm" %>
    <%= label f, :user_firm, "Firm" %>
  </div>
  ...

With those changes the radio button selection, "lawyer" or "firm", is saved to the type field when a new user is created.

1

1 Answers

2
votes

I think there are probably a few possibilities.

First, you could change the value attribute of the <radio /> elements to have differing values. Right now they both have the value true which means no matter which is selected, the "type" param that is sent to your controller will always have the value of true which means you have no way of knowing which radio button was selected. You could change the values to be something like value="firm" and value="lawyer", so that the "type" param in the controller would instead hold the string "firm" or "lawyer" instead of just always being "true".

A second option comes to mind, though, if these truly are mutually exclusive options. Just store a single column in the database instead of two. This could come in several possible incantations.

  • Maybe a boolean column called lawyer that is true if they are a lawyer, and if false, you know they are a firm instead. In this case, the radio button for lawyer would have value="true" and the radio button for firm would have value="false"
  • Change the database field to an enum type where you map the value to known types (lawyers, firms, etc...) This would allow you to also expand the list of types in future, should you need to. Then your radio button values are just the "enum" values that you are using.