I struggle to figure out an issue with a form validation.
I have a phoenix liveview form that containts a nested model. I render the form like this:
<.simple_form for={@form} id="specialist_sign_up_job_form" phx-submit="submit_fields" phx-change="update">
....
<.inputs_for :let={adr} field={@form[:address]}>
<.input field={adr[:county]} type="select" id="address-county" label={gettext("Judet")} options={@counties} prompt={gettext("Alegeti judetul")} phx-change="load_cities" />
<.input field={adr[:city]} type="select" id="address-city" label={gettext("Oras")} options={@cities} prompt={gettext("Alegeti orasul")} />
<.input field={adr[:street]} type="text" id="address-street" parent_class="md:col-span-2" class="border border-brd text-gray-500 text-sm rounded-xl focus:ring-primary-600 focus:border-primary-600 block w-full px-3 py-4" label={gettext("Adresa")} phx-debounce="400" />
<.input id="address-id" field={adr[:id]} type="hidden" />
<.input id="address-country" field={adr[:country]} type="hidden" value={gettext("Romania")} />
<.input id="address-lat" field={adr[:lat]} type="hidden" />
<.input id="address-lng" field={adr[:lng]} type="hidden" />
</.inputs_for>
<./simple_form>
My model
defmodule Recenza.Models.Specialist do
schema "specialists" do
.......
belongs_to(:address, Address, foreign_key: :address_id)
end
def job_data_changeset(specialist, attrs, validate_required \\ true) do
ch =
specialist
|> cast(attrs, @job_allowed_fields)
|> maybe_validate_required_company(specialist)
if validate_required do
ch
|> cast_assoc(:address, required: true)
|> put_assoc(:speciality, parse_speciality(attrs), required: true)
|> validate_required(@job_required_fields)
|> geolocate_address()
else
ch
|> cast_assoc(:address)
|> put_assoc(:speciality, parse_speciality(attrs))
end
end
end
and the association model
defmodule Recenza.Models.Address do
......
def changeset(address, attrs) do
address
|> cast(attrs, @allowed_fields)
|> validate_required(@required_fields)
end
end
In PhenixLiveview process I am have this code:
def handle_event("submit_fields", %{"specialist" => specialist_params}, socket) do
user = socket.assigns.current_user
specialist = user.specialist
changeset = Specialist.job_data_changeset(specialist, specialist_params, true)
case Repo.insert_or_update(changeset) do
{:ok, specialist} ->
socket = assign(socket, :current_user, %User{user | specialist: specialist})
{:noreply,
socket
|> put_flash(:info, "specialist job data data saved")
|> push_navigate(to: ~p"/specialist_sign_up_services")}
{:error, changeset} ->
{:noreply, assign(socket, :form, to_form(changeset, as: :specialist))}
end
end
I am printing in console the form after each call to server side and the form attribute looks like this:
UDATED CHANGESET: #Ecto.Changeset<
action: :update,
changes: %{
address: #Ecto.Changeset<
action: :insert,
changes: %{country: "România"},
errors: [
street: {"can't be blank", [validation: :required]},
city: {"can't be blank", [validation: :required]},
county: {"can't be blank", [validation: :required]}
],
data: #Recenza.Models.Address<>,
valid?: false
>,
employment_status: "employee",
speciality: #Ecto.Changeset<
action: :insert,
changes: %{},
errors: [
name: {"can't be blank", [validation: :required]},
category_id: {"can't be blank", [validation: :required]}
],
data: #Recenza.Models.Speciality<>,
valid?: false
>
},
errors: [
description: {"can't be blank", [validation: :required]},
company_name: {"Compania este necesara pentru angajati", []}
],
data: #Recenza.Models.Specialist<>,
valid?: false
>
As you can see there are validation errors on base model and also on the association.
The problem is that the UI does not render the errors on the address, only on specialist:
This happens every time I complete any of the field from specialist. If I do not complete any field and just try to submit the form as is I can see the errors on the address too:
I spent a lot of time trying it figure this out but I could now. Can you guys give me any hint on what I am missing??
I guess it’s something related to fact that the form does not see the nested object as changed but I am stuck at this point