-
Notifications
You must be signed in to change notification settings - Fork 215
/
form_data.ex
148 lines (127 loc) · 4.24 KB
/
form_data.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
defprotocol Phoenix.HTML.FormData do
@moduledoc """
Converts a data structure into a `Phoenix.HTML.Form` struct.
"""
@doc """
Converts a data structure into a `Phoenix.HTML.Form` struct.
The options are the same options given to `form_for/4`. It
can be used by implementations to configure their behaviour
and it must be stored in the underlying struct, with any
custom field removed.
"""
@spec to_form(t, Keyword.t()) :: Phoenix.HTML.Form.t()
def to_form(data, options)
@doc """
Converts the field in the given form based on the data structure
into a list of `Phoenix.HTML.Form` structs.
The options are the same options given to `inputs_for/4`. It
can be used by implementations to configure their behaviour
and it must be stored in the underlying struct, with any
custom field removed.
"""
@spec to_form(t, Phoenix.HTML.Form.t(), Phoenix.HTML.Form.field(), Keyword.t()) ::
[Phoenix.HTML.Form.t()]
def to_form(data, form, field, options)
@doc """
Returns the value for the given field.
"""
@spec input_value(t, Phoenix.HTML.Form.t(), Phoenix.HTML.Form.field()) :: term
def input_value(data, form, field)
@doc """
Returns the HTML5 validations that would apply to
the given field.
"""
@spec input_validations(t, Phoenix.HTML.Form.t(), Phoenix.HTML.Form.field()) :: Keyword.t()
def input_validations(data, form, field)
@doc """
Receives the given field and returns its input type (:text_input,
:select, etc). Returns `nil` if the type is unknown.
"""
@spec input_type(t, Phoenix.HTML.Form.t(), Phoenix.HTML.Form.field()) :: atom | nil
def input_type(data, form, field)
end
defimpl Phoenix.HTML.FormData, for: Plug.Conn do
def to_form(conn, opts) do
{name, params, opts} =
case Keyword.pop(opts, :as) do
{nil, opts} ->
{nil, conn.params, opts}
{name, opts} ->
name = to_string(name)
{name, Map.get(conn.params, name) || %{}, opts}
end
{errors, opts} = Keyword.pop(opts, :errors, [])
%Phoenix.HTML.Form{
source: conn,
impl: __MODULE__,
id: name,
name: name,
params: params,
data: %{},
errors: errors,
options: opts
}
end
def to_form(conn, form, field, opts) when is_atom(field) or is_binary(field) do
{default, opts} = Keyword.pop(opts, :default, %{})
{prepend, opts} = Keyword.pop(opts, :prepend, [])
{append, opts} = Keyword.pop(opts, :append, [])
{name, opts} = Keyword.pop(opts, :as)
{id, opts} = Keyword.pop(opts, :id)
id = to_string(id || form.id <> "_#{field}")
name = to_string(name || form.name <> "[#{field}]")
params = Map.get(form.params, field_to_string(field))
cond do
# cardinality: one
is_map(default) ->
[
%Phoenix.HTML.Form{
source: conn,
impl: __MODULE__,
id: id,
name: name,
data: default,
params: params || %{},
options: opts
}
]
# cardinality: many
is_list(default) ->
entries =
if params do
params
|> Enum.sort_by(&elem(&1, 0))
|> Enum.map(&{nil, elem(&1, 1)})
else
Enum.map(prepend ++ default ++ append, &{&1, %{}})
end
for {{data, params}, index} <- Enum.with_index(entries) do
index_string = Integer.to_string(index)
%Phoenix.HTML.Form{
source: conn,
impl: __MODULE__,
index: index,
id: id <> "_" <> index_string,
name: name <> "[" <> index_string <> "]",
data: data,
params: params,
options: opts
}
end
end
end
def input_value(_conn, %{data: data, params: params}, field)
when is_atom(field) or is_binary(field) do
case Map.fetch(params, field_to_string(field)) do
{:ok, value} ->
value
:error ->
Map.get(data, field)
end
end
def input_type(_conn, _form, _field), do: :text_input
def input_validations(_conn, _form, _field), do: []
# Normalize field name to string version
defp field_to_string(field) when is_atom(field), do: Atom.to_string(field)
defp field_to_string(field) when is_binary(field), do: field
end