lifeofdan

lifeofdan

Form errors not being shown

I am having an issue where form field errors are not being displayed under the following scenario. Load the page, fill one field of the form, click “submit”. The errors are being shown on the struct but there is no error on the form fields. However, if you just reload the page and click submit without touching any of the inputs you get the “is required” errors. Validation on individual fields while filling out the form also works as expected. Here is my resource.

defmodule App.Public.Contact do
  use Ash.Resource,
    domain: App.Public,
    authorizers: [Ash.Policy.Authorizer],
    data_layer: AshPostgres.DataLayer,
    extensions: [AshJsonApi.Resource]

  require Ash.Expr

  json_api do
    type "contact"
  end

  postgres do
    table "public_contacts"
    repo App.Repo
  end

  actions do
    defaults [:read, :destroy, update: :*]

    create :send_message do
      accept [:email, :message, :name]

      validate match(
                 :email,
                 ~r<^[a-zA-Z2-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$>
               ) do
        message "invalid email"
      end
    end
  end

  policies do
    policy action(:send_message) do
      authorize_if always()
    end

    policy action_type(:read) do
      authorize_if expr(^actor(:kind) == :manager)
    end
  end

  attributes do
    uuid_primary_key :id

    attribute :email, :ci_string, allow_nil?: false, public?: true
    attribute :message, :string, allow_nil?: false, public?: true
    attribute :name, :string, allow_nil?: false, public?: true

    create_timestamp :created_at
    update_timestamp :updated_at
  end
end

Here is my form

defmodule App.ContactLive do
  use AppWeb, :live_view

  @impl true
  def render(assigns) do
    ~H"""
    <div class="grid grid-cols-1 md:grid-cols-2 gap-4 my-8">
      <div class="text-gray-700">
        <.simple_form id="contact-form" for={@form} phx-submit="submit" phx-change="validate">
          <div class="grid sm:grid-cols-2 gap-10">
            <div>
              <.input
                type="text"
                class={@disabled_loading_state}
                field={@form[:name]}
                placeholder="John Doe"
                label="Name"
              />
            </div>
            <div>
              <.input
                type="email"
                class={@disabled_loading_state}
                field={@form[:email]}
                placeholder="john@example.com"
                label="Email"
              />
            </div>
          </div>
          <.input
            class={@disabled_loading_state}
            field={@form[:message]}
            placeholder="Enter your message here..."
            label="Message"
            type="textarea"
          />
          <:actions>
            <.button phx-disable-with="Sending..." class="phx-submit-loading:cursor-not-allowed">
              Submit
            </.button>
          </:actions>
        </.simple_form>
      </div>
      <div class="mx-4 md:border-l border-gray-400 bg-gray-200 h-screen">
        <div class="text-center mx-4 lg:mx-12 md:px-4 flex items-stretch">
          <div class="md:h-72 relative">
            <div class="relative md:top-1/2">
              <h1 class="leading-none mb-16">We want to hear from you!</h1>
            </div>
          </div>
        </div>
      </div>
    </div>
    """
  end

  @impl true
  def mount(_params, _session, socket) do
    form =
      App.Public.Contact
      |> AshPhoenix.Form.for_create(:send_message, as: "contact")
      |> to_form()

    disabled_loading_state =
      "phx-submit-loading:cursor-not-allowed phx-submit-loading:bg-slate-50 phx-submit-loading:text-slate-500 phx-submit-loading:border-slate-200"

    {:ok,
     socket
     |> assign(:disabled_loading_state, disabled_loading_state)
     |> assign(:form, form)}
  end

  @impl true
  def handle_event("validate", params, socket) do
    validate = AshPhoenix.Form.validate(socket.assigns.form, params["contact"])

    {:noreply, socket |> assign(form: validate)}
  end

  @impl true
  def handle_event("submit", params, socket) do
    case AshPhoenix.Form.submit(socket.assigns.form, params: params["contact"]) do
      {:ok, form} ->
        {:noreply,
         socket
         |> put_flash(:info, "Thank you for your message #{form.email}!")}

      {:error, form} ->
        {:noreply, socket |> assign(form: form)}
    end
  end
end

Is this expected behaviour or is this a bug? Also, is there a way I can manually take over this error behaviour when required? Any help appreciated, this is my first attempt using phoenix liveview so I accept I could be doing something wrong.

Thanks in advance.

Marked As Solved

zachdaniel

zachdaniel

Creator of Ash

Hmm…I think there was a recent change that only shows errors on touched fields, could that be part of the error that you’re seeing? There would be some code in your core components file like

errors = if Phoenix.Component.used_input?(field), do: field.errors, else: []

If you remove that and just do errors = field.errors, does it behave more as you’d expect?

Also Liked

lifeofdan

lifeofdan

I checked out main and it does now, indeed, work as expected. (once I un-delete the errors = if Phoenix.Component.used_input?(field), do: field.errors, else: []). Now I can delete a ton of code :+1:. Thanks to all those who continued to follow this up even after I had decided to just go my own way.

It is also interesting that this bugfix was merged into main on Sept 9th but still hasn’t been put into a release yet. This seems like something that everyone would be running into at the moment if they have initialized a new project. Glad it is fixed though, and I guess I will run on main branch until this is merged into a release.

lifeofdan

lifeofdan

Thank you! This doesn’t exactly give me the behaviour that I want but it puts me exactly where I need to be to get what I want. Thank you so much, I knew it had to be something simple like this!

zachdaniel

zachdaniel

Creator of Ash

There is some interesting behavior here, and using a repro from @carlgleisner I’ve ultimately decided to open a bug on LiveView. I could be wrong, it could be something wrong with AshPhoenix.Form, but I have not been able to identify what that would be.

Where Next?

Popular in Questions Top

New
Tee
can someone please explain to me how Enum.reduce works with maps
New
qwerescape
Is there a way to get the call stack or stack trace at any point in the code? Not from exceptions, but an expression that returns how the...
New
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? Ecto.Repo — Ecto v3.14.0 has exampl...
New
ycv005
I have followed this StackOverflow post to install the specific version of Erlang. And When I am running mix ecto.setup then getting fol...
New
jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
New
beno
I will often find my self writing things similar to: case some_value do nil -&gt; something() "" -&gt; something() _ -&gt; somethi...
New
SoCreat
i’m a new one to elixir which editor can i use vs code? or atom? Thanks! :smiley:
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New

Other popular topics Top

New
Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID&lt;0.412.0&gt; terminating ** (Postgrex.Error) FATAL...
New
minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
New
grych
Hi folks, Few months ago I have announced the proof-of-concept of the library to manipulate the browsers DOM objects directly from Elixi...
639 52341 488
New
AstonJ
Please see the new poll here: Which code editor or IDE do you use? (Poll) (2022 Edition) It’s been a while since we first asked this, I...
208 31142 143
New
rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list. ...
New
svb
Hi! Currently I want to submit a form by pressing the Enter key. However, since my input field is of type “textarea” this is just adds a...
New

We're in Beta

About us Mission Statement