owenfi

owenfi

Validate (to uppercase) and reset (when manually modified) not working as expected

To begin: I’m fairly new at web-frontend and trying to learn by cloning a popular word puzzle game.

I have an input field where a guess can be entered, in a manually crafted form in a liveview. It’s possible to enter text via real-keyboard or an onscreen rendered keyboard.

At first I was using <.input... but was having trouble styling it (not respecting class, is this expected?) so I changed to just a raw <input... so the form code is basically:

<.form id="game-guess" for={@form} phx-change="validate" phx-submit="save">
  <input type="text" id="guess-id" class="guess-box" value={@guess} field="guess" name="guess" />
  <button type="reset" class="delete-button">X</button>
</.form>

I’m attempting to convert all guesses to uppercase only, and limit to 5 characters, so my validate looks like:

def handle_event("validate", payload, socket) do
  newguess = String.upcase(payload["guess"])
  |> String.slice(0, 5)

  socket =
    assign(socket,
      guess: newguess,
      form: to_form(%{
        "guess" => newguess
      })
    )
  {:noreply, socket}
end

newguess looks correct when I inspect it, but the form doesn’t respect that / validate doesn’t work at all. I’m certain I shouldn’t be using the value={@guess} directly in the assigns (which is easy enough to remove by using value={"#{@form.params["guess"]}"} but that feels like basically the same hack) and that I am doing something wrong in the form creation, but haven’t been able to figure out what I should do. For a while I thought it was an issue with web browsers respecting capitalization changes, but the truncation also doesn’t work.

If I manually set the assigns.guess from my onscreen keyboard, then the reset button doesn’t work. I suspect they’re related.

I think my main question is: How do I update a form’s input field value in a validate so it is reflected in the view immediately?

First 5 of 5 Posts Switch mode

Hermanverschooten

Hermanverschooten

You don’t need the interpolation, value={@form.params["guess"]} will do just fine, maybe value={@form["guess"].value} is more idiomatic, Phoenix.HTML.FormField — Phoenix.HTML v3.3.0

garrison

garrison

The value is not being overwritten because LiveView does not overwrite the contents of inputs that have focus. See the docs here:

If it worked that way, you would run into all sorts of wonky behavior due to latency, because the server’s state would always be behind the client, so every time an update came back it would delete some of your text as you type.

For this sort of thing (modifying input as a user types), you can use a JS hook and some standard JavaScript to listen to the input event and uppercase the string synchronously on the client. You can find the docs for hooks here:

Then if for some reason you need to validate that the field is uppercase on the server, you can just check it on submit and throw an error (since only a malicious user would send a lowercase string to the server).

garrison

garrison

Oh, and also, the reason you can’t style the input component is because it comes pre-styled with tailwind classes and so it doesn’t pass the @class attribute in by default. But these components are literally generated into your project (they’re in core_components.ex), so you can change them to whatever you want.

What you did (styling a one-off input) is also fine - it just depends on what you’re doing. If you want to re-use that input elsewhere in the project you can turn it into another component.

owenfi

owenfi OP

Thanks for all the answers everyone! I updated to not use interpolation and it does look cleaner, but no behavior change.

The explanation about not modifying fields with focus makes complete sense, thank you for all the docs pointers! (Will try the JS Hook approach soon)

Also the styling being baked into core components is nice, too. I figured these were functions defined somewhere but thought it was at the framework level and they weren’t opening in my editor automagically so I stopped hunting for them after a bit. (Though I had read that “daunting” file a bit in the past, just forgot about it.) Would it be a bad practice to customize this version to allow passing in class definitions to append?

Finally, for the reset button: Is it possible to wire things up in a way that the default <button type="reset"... works for manually entered values? Or should I just hook that to another action and handle it directly (this seems simple and correct enough).

garrison

garrison

It’s fine - it depends on context. Obviously the more customization you add the harder the component will be to re-use. Especially with Tailwind, since you have to pass in the universe in order to style a component. You can also accept variables as assigns and then use them to dynamically choose the relevant classes (e.g. for color or size). I use that pattern a lot (though I don’t use Tailwind).

Buttons with type="reset" are pretty rare and generally not recommended, for various reasons. I think in this case the problem is that Phoenix is injecting the current value of the input into the value="..." attr, which is used to reset the input (so it just gets reset to the server value). You would have the same problem, I think, with a React controlled input.

Implementing the reset functionality in code should fix that. It should work server-side because clicking the button will steal focus from the input allowing it to be reset.

— All posts loaded —

Where Next?

Trending in Questions Top

stjefim
Hello! Suppose you are building workflow (order / task / payment) processing system with the following requirements: Each workflow con...
New
jonnycharles
I’m in search of an Elixir library that offers PDF generation capabilities similar to Ruby’s Prawn. While there have been discussions abo...
New
spammy
I’m looking to build a personal workflow to quickly deploy web applications written in elixir/phoenix, for local consumption (ie not on t...
New
silverdr
Using Phoenix.LiveView.TagEngine as an EEx.Engine is deprecated! To compile HEEx, use Phoenix.LiveView.TagEngine.compile/2 instead. Sta...
New
dli
Before I dive in myself, did anyone successfully sprinkle Hologram into their existing LiveView app? Looking for hints regarding: Addi...
New
bottlenecked
Hi all, I wanted to ask how the community is dealing with post-release steps. Today we have Ecto migrations, which make sure that the db...
New
michallepicki
I am using Oban and occasionally, shortly after a deployment, a handful of jobs can fail because of dependency on other parts of the syst...
New

Other Trending Topics Top

JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge &amp; Solve. They are GUI (Emerge) and State management (S...
New
ausimian
Emily is an Elixir library that runs Nx computations on Apple’s MLX. Install it as the default Nx backend and Nx, defn, Axon, Nx.Serving,...
New
type1fool
I just stumbled on a newly redesigned elixir-lang.org. :tada: It looks like @Software_Mansion did the work, and I think it is generally a...
New
akoutmos
@hugobarauna and I (Alex Koutmos) have been hard at work on writing a book on Nerves that takes you from simply blinking LEDs to building...
New

We're in Beta

About us Mission Statement