Form drop down values dependent on form text input

Hi,

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>
       <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 %>
      </div>

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

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} =
       index_live
       |> 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

Thanks! Now the tests pass!