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 "e;ernie"e;
</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 unsolved
s?