Live validation with turbo and stimulus

Hi,

I wanted to build a live form validation - inspired by phoenix live view. But my backbone technology should be java + spring boot + thymeleaf to serve customers with conservative stacks. But that should not matter for turbo and stimulus.

The validation should be done on keyup in a field. So this is my code:

The showcase form:

<!DOCTYPE html>
<html lang="de">
<head>
    <meta charset="UTF-8">
    <title>Register</title>
    <link href="app.css" rel="stylesheet"/>
    <script src="app.js" defer></script>
</head>
<body>

<turbo-frame id="registration-form">
    <form id="register"
          action="/register/new"
          method="post"
          data-controller="form-validation">
        <div>
            <label for="registration_display-name">display name:</label>
            <input id="registration_display-name"
                   name="registration_display-name"
                   value=""
                   data-action="keyup->form-validation#validate">
            <turbo-frame id="registration_display-name_error">
            </turbo-frame>
        </div>
        <div>
            <label for="registration_name">Name:</label>
            <input id="registration_name"
                   name="registration_name"
                   value=""
                   data-action="keyup->form-validation#validate">
            <turbo-frame id="registration_name_error">
            </turbo-frame>
        </div>
        <div>
            <button type="submit"
                    formaction="/register/validate"
                    data-form-validation-target="validator"
                    data-turbo-frame="registration_display-name_error, registration_name_error">
                validate
            </button>
        </div>
        <div>
            <button type="submit">register</button>
        </div>
    </form>
</turbo-frame>

</body>
</html>

The form-validation stimulus controller:

import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
    static targets = [ "validator" ]

    validate(e) {
        e.preventDefault()
        this.validatorTarget.click()
    }
}

http the response from post to /register/validate:

<!DOCTYPE html>
<html lang="de">
<head>
    <meta charset="UTF-8">
    <title>Register</title>
    <link href="app.css" rel="stylesheet"/>
    <script src="app.js" defer></script>
</head>
<body>

<turbo-frame id="registration-form">
    <form id="register"
          action="/register/new"
          method="post"
          data-controller="form-validation">
        <div>
            <label for="registration_display-name">display name:</label>
            <input id="registration_display-name"
                   name="registration_display-name"
                   value="r"
                   data-action="keyup->form-validation#validate">
            <turbo-frame id="registration_display-name_error">
                <p class="error">min length 3</p>
            </turbo-frame>
        </div>
        <div>
            <label for="registration_name">Name:</label>
            <input id="registration_name"
                   name="registration_name"
                   value=""
                   data-action="keyup->form-validation#validate">
            <turbo-frame id="registration_name_error">
                <p class="error">min length 5</p>
            </turbo-frame>
        </div>
        <div>
            <button type="submit"
                    formaction="/register/validate"
                    data-form-validation-target="validator"
                    data-turbo-frame="registration_display-name_error, registration_name_error">
                validate
            </button>
        </div>
        <div>
            <button type="submit">register</button>
        </div>
    </form>
</turbo-frame>

</body>
</html>

the http response on a valid submit:

<!DOCTYPE html>
<html lang="de">
<head>
    <meta charset="UTF-8">
    <title>Register</title>
    <link href="app.css" rel="stylesheet"/>
    <script src="app.js" defer></script>
</head>
<body>

<turbo-frame id="registration-form">
    Registered with name &quote;ernie&quote;
</turbo-frame>

</body>
</html>

Solved problems:

  • If the errors where not in a special frame, I would lose the focus on the current input. So I used special frames for the error messages and a data-turbo-frame on the validation-submit button to target only these frames (great feature by the way).

Still unsolved:

  • I need to address more than one target frames
  • a stimulus debounce would be nice.
  • errors should only be shown for visited form fields.

I got stuck here. Can someone help me with one of the three unsolveds?