So I have a form which validates on change.
def handle_event("validate", %{"form" => params}, socket) do
form = AshPhoenix.Form.validate(socket.assigns.form, params)
{:noreply, assign(socket, form: form)}
end
While trying to read the value of the following field I noticed it did not change until it was valid. The field is on a nested form. I have not shown the root form.
%Phoenix.HTML.FormField{
id: "reactant_formulas_0_formula_0_reactant_0_identity",
name: "reactant[formulas][0][formula][0][reactant][identity]",
errors: [{"is required", []}],
field: :identity,
form: %Phoenix.HTML.Form{
source: #AshPhoenix.Form<
resource: Flame.App.Reactant,
action: :create,
type: :create,
params: %{"_form_type" => "create"},
source: #Ash.Changeset<
action_type: :create,
action: :create,
attributes: %{},
relationships: %{},
errors: [
%Ash.Error.Changes.Required{
field: :identity,
type: :attribute,
resource: Flame.App.Reactant,
changeset: nil,
query: nil,
error_context: [],
vars: [],
path: [],
stacktrace: #Stacktrace<>,
class: :invalid
}
],
data: #Flame.App.Reactant<
sources: #Ash.NotLoaded<:relationship>,
formulas: #Ash.NotLoaded<:relationship>,
sources_join_assoc: #Ash.NotLoaded<:relationship>,
__meta__: #Ecto.Schema.Metadata<:built, "reactants">,
id: nil,
identity: nil,
spec: nil,
created_at: nil,
updated_at: nil,
aggregates: %{},
calculations: %{},
...
>,
valid?: false
>,
name: "reactant[formulas][0][formula][0][reactant]",
data: nil,
form_keys: [],
forms: %{},
api: nil,
method: "post",
submit_errors: nil,
id: "reactant_formulas_0_formula_0_reactant",
transform_errors: nil,
original_data: nil,
transform_params: nil,
prepare_params: nil,
prepare_source: nil,
warn_on_unhandled_errors?: true,
any_removed?: false,
added?: true,
changed?: false,
touched_forms: MapSet.new(["_form_type"]),
valid?: false,
errors: true,
submitted_once?: false,
just_submitted?: false,
...
>,
impl: Phoenix.HTML.FormData.AshPhoenix.Form,
id: "reactant_formulas_0_formula_0_reactant_0",
name: "reactant[formulas][0][formula][0][reactant]",
data: nil,
hidden: [
{"_persistent_id", "0"},
{:_touched, "_form_type"},
{:_form_type, "create"}
],
params: %{"_form_type" => "create", "_persistent_id" => "0"},
errors: [identity: {"is required", []}],
options: [method: "post"],
index: nil,
action: nil
},
value: nil
}
The field is for the following attribute.
attribute :identity, :string do
allow_nil? false
constraints trim?: false, max_length: 20, min_length: 4, allow_empty?: false
end
Upon inputting t
into the text input for identity
the field becomes the following after validation.
%Phoenix.HTML.FormField{
id: "reactant_formulas_0_formula_0_reactant_0_identity",
name: "reactant[formulas][0][formula][0][reactant][identity]",
errors: [
{"length must be greater than or equal to %{min}",
[
field: "identity",
message: "length must be greater than or equal to %{min}",
min: 4
]}
],
field: :identity,
form: %Phoenix.HTML.Form{
source: #AshPhoenix.Form<
resource: Flame.App.Reactant,
action: :create,
type: :create,
params: %{
"_form_type" => "create",
"_persistent_id" => "0",
"_touched" => "_form_type",
"identity" => "t"
},
source: #Ash.Changeset<
action_type: :create,
action: :create,
attributes: %{},
relationships: %{},
errors: [
%Ash.Error.Changes.InvalidAttribute{
field: :identity,
message: "length must be greater than or equal to %{min}",
private_vars: nil,
value: "t",
changeset: nil,
query: nil,
error_context: [],
vars: [
field: :identity,
message: "length must be greater than or equal to %{min}",
min: 4
],
path: [],
stacktrace: #Stacktrace<>,
class: :invalid
}
],
data: #Flame.App.Reactant<
sources: #Ash.NotLoaded<:relationship>,
formulas: #Ash.NotLoaded<:relationship>,
sources_join_assoc: #Ash.NotLoaded<:relationship>,
__meta__: #Ecto.Schema.Metadata<:built, "reactants">,
id: nil,
identity: nil,
spec: nil,
created_at: nil,
updated_at: nil,
aggregates: %{},
calculations: %{},
...
>,
valid?: false
>,
name: "reactant[formulas][0][formula][0][reactant]",
data: nil,
form_keys: [],
forms: %{},
api: nil,
method: "post",
submit_errors: nil,
id: "reactant_formulas_0_formula_0_reactant",
transform_errors: nil,
original_data: nil,
transform_params: nil,
prepare_params: nil,
prepare_source: nil,
warn_on_unhandled_errors?: true,
any_removed?: false,
added?: true,
changed?: false,
touched_forms: MapSet.new(["_form_type", "_persistent_id", "_touched",
"identity"]),
valid?: false,
errors: true,
submitted_once?: false,
just_submitted?: false,
...
>,
impl: Phoenix.HTML.FormData.AshPhoenix.Form,
id: "reactant_formulas_0_formula_0_reactant_0",
name: "reactant[formulas][0][formula][0][reactant]",
data: nil,
hidden: [
{"_persistent_id", "0"},
{:_touched, "_form_type,_persistent_id,_touched,identity"},
{:_form_type, "create"}
],
params: %{
"_form_type" => "create",
"_persistent_id" => "0",
"_touched" => "_form_type",
"identity" => "t"
},
errors: [
identity: {"length must be greater than or equal to %{min}",
[
field: "identity",
message: "length must be greater than or equal to %{min}",
min: 4
]}
],
options: [method: "post"],
index: nil,
action: nil
},
value: nil
}
The value:
remains nil
I would have expected it to become "t"
. "t"
can be seen in the params:
of the form:
. The error is because the value is of insufficient length. If I continue to type e
and s
the value:
continues to be nil
with params:
updating. If I then type t
now we get the following field.
%Phoenix.HTML.FormField{
id: "reactant_formulas_0_formula_0_reactant_0_identity",
name: "reactant[formulas][0][formula][0][reactant][identity]",
errors: [],
field: :identity,
form: %Phoenix.HTML.Form{
source: #AshPhoenix.Form<
resource: Flame.App.Reactant,
action: :create,
type: :create,
params: %{
"_form_type" => "create",
"_persistent_id" => "0",
"_touched" => "_form_type,_persistent_id,_touched,identity",
"identity" => "test"
},
source: #Ash.Changeset<
action_type: :create,
action: :create,
attributes: %{identity: "test"},
relationships: %{},
errors: [],
data: #Flame.App.Reactant<
sources: #Ash.NotLoaded<:relationship>,
formulas: #Ash.NotLoaded<:relationship>,
sources_join_assoc: #Ash.NotLoaded<:relationship>,
__meta__: #Ecto.Schema.Metadata<:built, "reactants">,
id: nil,
identity: nil,
spec: nil,
created_at: nil,
updated_at: nil,
aggregates: %{},
calculations: %{},
...
>,
valid?: true
>,
name: "reactant[formulas][0][formula][0][reactant]",
data: nil,
form_keys: [],
forms: %{},
api: nil,
method: "post",
submit_errors: nil,
id: "reactant_formulas_0_formula_0_reactant",
transform_errors: nil,
original_data: nil,
transform_params: nil,
prepare_params: nil,
prepare_source: nil,
warn_on_unhandled_errors?: true,
any_removed?: false,
added?: true,
changed?: true,
touched_forms: MapSet.new(["_form_type", "_persistent_id", "_touched",
"identity"]),
valid?: true,
errors: true,
submitted_once?: false,
just_submitted?: false,
...
>,
impl: Phoenix.HTML.FormData.AshPhoenix.Form,
id: "reactant_formulas_0_formula_0_reactant_0",
name: "reactant[formulas][0][formula][0][reactant]",
data: nil,
hidden: [
{"_persistent_id", "0"},
{:_touched, "_form_type,_persistent_id,_touched,identity"},
{:_form_type, "create"}
],
params: %{
"_form_type" => "create",
"_persistent_id" => "0",
"_touched" => "_form_type,_persistent_id,_touched,identity",
"identity" => "test"
},
errors: [],
options: [method: "post"],
index: nil,
action: nil
},
value: "test"
}
The error is gone and the value:
is finally updated to "test"
. I am trying to read the value from the field as it changes. I am passing the field to a LiveComponent and trying to access and alter the value:
but it only changes when the error is gone. Should I instead be getting the value from the params:
? I think it’s a little confusing that the value:
only updates if there are no errors.