Repository: phoenixframework/phoenix_html (elixir)

Issue:
input_id/3 argument error when used in a form without :as option
At line 388 in `form.ex`, the `input_id/3` function returns:

```elixir 
input_id(name, field) <> "_" <> value_id
```

This raises an ArgumentError when `input_id(name, field)` returns `nil`, because it cannot be concatenated with the rest of the string.  

Despite the fact that the function specification for `input_id/2` says that it should return a string, it will actually return `nil` when the first argument is a `Phoenix.HTML.Form` that has `id: nil` (line 368):

```elixir 
@spec input_id(t | atom, field) :: String.t()
def input_id(%{id: nil}, _field), do: nil
```

And a form's `id` will be `nil` if the form was created without the `:as` option being set.

Since `input_id/3` is called within `radio_button/4`, this error occurs when trying to add a radio button within a form that does not have the `:as` option set.

This could be fixed either by making sure `input_id/2` always returns a string (as per the spec), or by checking `input_id/2`'s return type when it's called within `input_id/3` and handling accordingly.


Pull request description:
Make input_id/2 return the field name when the form has no id
Fixes #222 

When `input_id/2` is passed a form with no `id`, it now returns the name of the field rather than `nil`.

Patch:
diff --git a/lib/phoenix_html/form.ex b/lib/phoenix_html/form.ex
index 16bc65b..080b129 100644
--- a/lib/phoenix_html/form.ex
+++ b/lib/phoenix_html/form.ex
@@ -366,7 +366,7 @@ defmodule Phoenix.HTML.Form do
   by `form_for` or an atom.
   """
   @spec input_id(t | atom, field) :: String.t()
-  def input_id(%{id: nil}, _field), do: nil
+  def input_id(%{id: nil}, field), do: "#{field}"
 
   def input_id(%{id: id}, field) when is_atom(field) or is_binary(field) do
     "#{id}_#{field}"


Test patch:
diff --git a/test/phoenix_html/form_test.exs b/test/phoenix_html/form_test.exs
index 5f2e05b..3a5d863 100644
--- a/test/phoenix_html/form_test.exs
+++ b/test/phoenix_html/form_test.exs
@@ -109,7 +109,7 @@ defmodule Phoenix.HTML.FormTest do
         end)
       )
 
-    assert form =~ ~s(<input name="key" type="text">)
+    assert form =~ ~s(<input id="key" name="key" type="text">)
   end
 
   ## text_input/3
@@ -804,22 +804,22 @@ defmodule Phoenix.HTML.FormTest do
              &multiple_select(&1, :key, [{"foo", 1}, {"bar", 2}], value: [1], selected: [2]),
              []
            ) ==
-             ~s(<select multiple="" name="key[]">) <>
+             ~s(<select id="key" multiple="" name="key[]">) <>
                ~s(<option value="1" selected>foo</option>) <>
                ~s(<option value="2">bar</option>) <> ~s(</select>)
 
     assert safe_form(&multiple_select(&1, :other, [{"foo", 1}, {"bar", 2}], selected: [2]), []) ==
-             ~s(<select multiple="" name="other[]">) <>
+             ~s(<select id="other" multiple="" name="other[]">) <>
                ~s(<option value="1">foo</option>) <>
                ~s(<option value="2" selected>bar</option>) <> ~s(</select>)
 
     assert safe_form(&multiple_select(&1, :key, [{"foo", 1}, {"bar", 2}], value: [2]), []) ==
-             ~s(<select multiple="" name="key[]">) <>
+             ~s(<select id="key" multiple="" name="key[]">) <>
                ~s(<option value="1">foo</option>) <>
                ~s(<option value="2" selected>bar</option>) <> ~s(</select>)
 
     assert safe_form(&multiple_select(&1, :key, ~w(value novalue), value: ["novalue"]), []) ==
-             ~s(<select multiple="" name="key[]">) <>
+             ~s(<select id="key" multiple="" name="key[]">) <>
                ~s(<option value="value">value</option>) <>
                ~s(<option value="novalue" selected>novalue</option>) <> ~s(</select>)
 
@@ -832,7 +832,7 @@ defmodule Phoenix.HTML.FormTest do
              ),
              []
            ) ==
-             ~s(<select multiple="" name="key[]">) <>
+             ~s(<select id="key" multiple="" name="key[]">) <>
                ~s(<option value="1">foo</option>) <>
                ~s(<option value="2">bar</option>) <>
                ~s(<option value="3" selected>goo</option>) <> ~s(</select>)
@@ -1250,6 +1250,10 @@ defmodule Phoenix.HTML.FormTest do
     assert safe_form(&input_id(&1, :key)) == "search_key"
   end
 
+  test "input_id/2 with form with no name" do
+    assert safe_form(&input_id(&1, :key), []) == "key"
+  end
+
   ## input_id/3
 
   test "input_id/3" do
@@ -1264,6 +1268,10 @@ defmodule Phoenix.HTML.FormTest do
     assert input_id(:search, :key, -1) == "search_key__1"
   end
 
+  test "input_id/3 with form with no name" do
+    assert safe_form(&input_id(&1, :key, :value), []) == "key_value"
+  end
+
   ## input_name/2
 
   test "input_name/2 without form" do