Packages
formex
0.6.7
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.10
0.5.9
0.5.8
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.16
0.4.15
0.4.14
0.4.13
0.4.12
0.4.11
0.4.10
0.4.9
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.3
0.3.2
0.3.1
0.3.0
0.2.3
0.2.2
0.2.1
0.2.0
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
Form library for Phoenix with Ecto support
Current section
Files
Jump to
Current section
Files
lib/formex/form_collection.ex
defmodule Formex.FormCollection do
alias __MODULE__
alias Formex.Field
alias Formex.Form
alias Formex.FormNested
defstruct forms: [],
struct_module: nil,
name: nil,
# added only for compability with validation libs which look for `struct_name`
struct_name: nil,
opts: [],
delete_field: nil,
validation: [],
type: nil
@type t :: %FormCollection{}
@moduledoc """
Form collections
To get started see `Formex.Type`, section _Collections of forms_.
Functionality that are not mentioned in the link above:
## Options
* `type` - module that implements `Formex.Type`. Required
* `struct_module` - module of struct, e.g. `App.UserAddress`
* `delete_field` - defaults to `:formex_delete`. Defines input that will be set to true
if we click ×. Behaviours depends on value:
* `:formex_delete` - if `formex_delete` is true, this collection item
will be removed.
* another field - it's a simple input that may be stored in repo
For example, we can in our model create `removed` field. Then set
`delete_field: :removed` option.
"""
@doc false
@spec start_creating(form :: Form.t(), type :: any, name :: Atom.t(), opts :: Map.t()) :: Map.t()
def start_creating(form, type, name, opts) do
{:collection, submodule} = form.struct_info[name]
submodule =
if submodule do
submodule
else
Keyword.get(opts, :struct_module) || raise "the :struct_module option is required"
end
{delete_field, opts} = Keyword.pop(opts, :delete_field)
%FormCollection{
name: name,
# struct_name: Keyword.get(opts, :struct_name, name), # not handled yet
struct_name: name,
type: type,
opts: opts,
struct_module: submodule,
validation: Keyword.get(opts, :validation, []),
delete_field: delete_field || :formex_delete
}
end
# called when substructs are ready
@doc false
@spec finish_creating(form :: Form.t(), form_collection :: FormCollection.t()) :: Form.t()
def finish_creating(form, form_collection) do
substructs = Field.get_value(form, form_collection.name)
# substructs = if opts[:filter] do
# Enum.filter(substructs, opts[:filter])
# else
# substructs
# end
params = form.params[to_string(form_collection.name)] || []
subforms_old = create_existing_subforms(form, form_collection, params, substructs)
subforms_new = create_new_subforms(form, form_collection, params)
form_collection
|> Map.put(:forms, subforms_old ++ subforms_new)
end
@doc false
@spec get_subform_by_struct(form_collection :: t, struct :: Map.t()) :: FormNested.t()
def get_subform_by_struct(form_collection, struct) do
form_collection.forms
|> Enum.find(fn form_nested ->
if struct.id && struct.id !== "" do
to_string(form_nested.form.struct.id) == to_string(struct.id)
else
form_nested.form.struct.formex_id == struct.formex_id
end
end)
end
defp create_existing_subforms(form, form_collection, params, substructs) do
substructs
|> Enum.map(fn substruct ->
{_, subparams} =
Enum.find(params, {nil, %{}}, fn {_k, v} ->
id =
if is_integer(substruct.id) do
v["id"] |> Integer.parse() |> elem(0)
else
v["id"]
end
substruct.id == id
end)
create_subform(form, form_collection, substruct, subparams)
end)
end
defp create_new_subforms(form, form_collection, params) do
%{struct_module: submodule} = form_collection
params
|> Enum.filter(fn {_key, val} -> !val["id"] end)
|> Enum.map(fn {key, val} -> {String.to_integer(key), val} end)
|> Enum.sort_by(fn {key, _} -> key end, fn key1, key2 -> key1 < key2 end)
|> Enum.map(fn {_key, subparams} ->
substruct = struct(submodule, formex_id: subparams["formex_id"])
create_subform(form, form_collection, substruct, subparams)
end)
end
defp create_subform(form, form_collection, substruct, subparams) do
%{name: name, type: type, struct_module: submodule, opts: opts} = form_collection
subform =
Formex.Builder.create_form(
type,
substruct,
subparams,
form.opts,
submodule
)
%FormNested{
form: subform,
name: name,
validation: Keyword.get(opts, :validation, []),
struct_module: submodule,
type: type,
opts: opts
}
end
@spec to_be_removed(form_collection :: t, form_nested :: FormNested.t()) :: boolean
@doc false
def to_be_removed(form_collection, form_nested) do
val = Map.get(form_nested.form.new_struct, form_collection.delete_field)
val == "true"
end
end