Form drop down values dependent on form text input


I need help with conditional form drop down menu. Right now, I have the following form

<div class="sm:flex sm:items-start justify-center w-80 min-w-full">
  <div class="w-80">
    <h2 class="modal-title"><%= @title %></h2>

    <%= f = form_for @changeset, "#",
      id: "post-form",
      phx_target: @myself,
      phx_change: "validate",
      phx_submit: "save",
      class: "mt-4" %>

      <div class="grid grid-cols-1 mb-4">
        <%= label f, :post_type %>
        <%= text_input f, :post_type, class: "form-text-input" %>
        <%= error_tag f, :post_type %>
       <div class="grid grid-cols-1 mb-4">
        <%= label f, :tag_color %>
        <%= label do %>
        <%= select f, :tag_color, [:red, :green] %>
        <% end %>
        <%= error_tag f, :tag_color %>

      <%= submit "Save", phx_disable_with: "Saving...", class: "btn-success" %>

I would like the tag_color drop down options to change dynamically depending on the post_type input. I mean there are many post types in my case like regular, news and attached content.

I would like to tag_color drop down to show [:red, :green] for all post_type inputs except attached_content which will need to have tag_color drop down with all three tag_color options [:red, :blue, :green]. How to set this conditionally? I am not sure where this logic should take place and how.

Thanks in advance :slight_smile:

Make tag_colour_options an assign and use it as the options arg of your select.

<%= select f, :tag_color, @tag_colour_options %>

In the validate event, check the value of post_type,

tag_colour_options = if post_type == "attached_content", do: [:red, :green, :blue], else: [:red, :green]

and assign the applicable tag_colour_options

{:noreply, assign(socket, :tag_colour_options, tag_colour_options}.


Thanks, this worked but when writing tests, the tests seem to be failing. One of the tests that fails is below:

{:ok, index_live, _html} = live(conn, Routes.blog_post_index_path(conn, :index))

      assert index_live |> element("a", "New Post") |> render_click() =~
               "New Post"

      assert_patch(index_live, Routes.blog_post_index_path(conn, :new))

      assert index_live
      |> form("#post-form", post: %{post_name: nil, post_type: "regular", tag_color: :green})
      |> render_change() =~ "can&#39;t be blank"

      {:ok, _, html} =
       |> form("#post-form", post: %{post_name: nil, post_type: "attached_content", tag_color: :blue})
       |> render_submit()
       |> follow_redirect(conn, Routes.blog_post_index_path(conn, :index))

If I debug and see, what happens is, first the form gets nil values for all fields since the form is being initiated, next a set of invalid attributes(i.e, post_name: nil) are passed to make sure the form returns the expected output. But after the render_change, when I pass the attrs with blue tag color, I get the following error:

** (ArgumentError) value for select "post[tag_color]" must be one of ["red", "green"], got: "blue"
     code: |> render_submit()

I believe this error is caused by render_change followed by render_submit. Any suggestion on how this can be resolved?

I believe you need to split your input into two steps. first input post_type: “attached_content” and then pipe to render_change. “blue” does not get added to your list of options until that change is handled by the live view. then you can pipe to input tag_color: “blue” and render_submit.

This may seem like extra steps but these are the same steps your live view is taking as you input the post type on the page

1 Like

Thanks! Now the tests pass!

1 Like