How to validate an input having phx-change attribut?

Hi,

I have a Live Component with an autocomplete search input. I’m using phx-change on the text input as an attribut to send an event to look up entries in the database matching the user input. I’m also using phx-change on the form level to real-time validating all the form inputs and that works well. However, the autocomplete input is not getting validated as it is overriding the behaviour of the phx-change to send a search event instead of validating process. The problem is that I set the autocomplete as a required field and if the user omit it or delete it’s input, it should then get an error feedback as with other required fields.

Here is my autocomplete LC and I’m using AlpineJs to open or close the list of matching entries :

def render(assigns) do
    ~H"""
      <div x-on:keyup.up="focusPrev" x-on:keyup.down="focusNext">
        <div>
          <label class="field_input_label required"><%= @label %></label>
          <%= text_input @form, @field, id: "search-input" , class: "field_input_text ", autocomplete: "off" ,
          phx_change: JS.push("search") , 'x-on:input.debounce.500ms': "open", 'x-on:keyup': "validate" %>
          <%= error_tag @form, @field %>
        </div>
        <ul x-show="isOpen" x-ref="suggestions @keyup.enter="select">
          <%= for {item, index} <- Enum.with_index(@items) do %>
            <li id={"item-#{index}"} x-ref={"item-#{index}"} @click="select()">
              <%= item %>
            </li>
          <% end %>
        </ul>
      </div>
    """
  end

Is there any way to force the input to be validated after the user has changed it ?
I tried this :

phx_change: JS.push("search")  |> phx_change: JS.push("validate") 

It works partially. The events are fired sequentially as expected but it validates only this input and ignores other changes from other inputs and they get reset !

I also tried to fire a validate event from Javascript but it doesn’t work and no error shows up :

#on LC
'x-on:keyup': "validate" 

#on javascript
validate() {
    search = document.getElementById("search-input");
    search.dispatchEvent(
      new Event("input", { bubbles: true })
    )
  },

Or maybe the only other solution might be to use phx-keyup instead of phx-change ?

Thanks for your help.

If you use phx-change on a specific input, only that input’s value will be sent to the server when the change event fires. So if you’re using this to trigger a search (which I assume will be used to fill your dropdown of selectable options) you can’t also use it to validate the entire form.

The autocomplete field shouldn’t probably be part of the form’s values that get validated. It’s a sort of “helper” input, only used to record what the user is typing and search for options. Those options can be selected, and the one the user select should be sent as part of the real form’s inputs. This could be accomplished using a hidden input.

Another possibility is having the autocomplete field be part of the validated form inputs, and selecting one of the options would replace the content of the field with the option and trigger a change. In this case however you can’t use phx-change to trigger the search (but you can use phx-keyup for example as you already mentioned). Also, you’ll have to trigger the change from JS.

Building an autocomplete input is difficult and painful. If you want to, you can try this component that I wrote: GitHub - maxmarcon/live_select: Dynamic (multi)selection field for LiveView

It allows you to add an autocomplete input to your form with a few lines of code. If you decide that it doesn’t suit your needs, I’d be interested in knowing why so I can improve it (the best thing would be to open an issue). Even so, you can use the code as an inspiration for your component. All the problems you’re trying to solve should have already been solved there.

I hope this helps!

@trisolaran thanks for your help. Ok, now I understand better the concept behind phx-change on a specific input. The reason why I want my input to be validated is because the end user should either select an option if it exists or create a new one (to be stored as new entry in database) if doesn’t. In any case the field can’t be blank.

I would have liked to know that your component exist ! because as you mentioned, it’s really difficult to build an autocomplete input as I spent a lot of time to build it, but I will take a look and see if it can fit better my need.

1 Like