Ah, good point.
Anyhow, for reference, and for stumblers across this post, here is the solution that I came up with. Not sure if it has flaws, any comments are welcome.
Key points:
- I partially utilized HTML5 client side validation.
- I replaced the <f:input> with vanilla <input type="submit">, and the input form to be validated (comments) with plain <input> (important that I added the name="comments" which matches the field name and class="jenkins-input" so that it looks nice. Without the input type replacements the html5 validator does not kick in, therefore it is important.
- In addition to the above, I added a "submit" event listener to the form (Vanilla JS). With the
preventDefault() method call I prevented the event "bubbling up" and submitting the form. In the handler method I ran my own custom (client side) validator.
- In addition, I run an additional server-side validator by utilizing the tooling called JavaScript proxy. (payload are the form fields. Parsing the JSON String is up to the server, use Jackson or whatever you like).
- If the server side validator returns anything else than "OK", it is displayed as an error message, else the form is actually submitted.
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<l:layout title="${%title}">
<l:main-panel>
<st:contentType value="text/html;charset=UTF-8"/>
<st:bind var="backingClass" value="${it}"/>
<f:form action="submit" method="post" name="release-build-form" id="mark-as-release-form">
<f:entry field="release">
<f:checkbox checked="${it.releaseBuildMarkForRun != null}" title="${%chkIsReleaseBuild}"/>
</f:entry>
<f:entry title="${%inputComments}" field="comments">
<input class="jenkins-input" name="comments" minlength="${it.commentsLengthMin}" maxlength="${it.commentsLengthMax}" required="true" />
</f:entry>
<input type="submit" value="${%btnSubmit}"/>
</f:form>
<script>
const handleFormSubmit = (event) => {
event.preventDefault();
const form = event.target;
console.log("Form: " + form);
const payload = {};
const formData = new FormData(form);
formData.forEach(function(value, key){
payload[key] = value;
});
backingClass.validateFormDataJS(JSON.stringify(payload), (t) => {
const responseMessage = t.responseObject();
if (responseMessage == "${it.JS_VALIDATOR_RESPONSE_OK}") {
form.submit();
} else {
notificationBar.show(responseMessage, notificationBar.ERROR);
}
});
}
const form = document.getElementById('mark-as-release-form');
form.addEventListener('submit', handleFormSubmit);
</script>
</l:main-panel>
</l:layout>
</j:jelly>
And in the Action:
@JavaScriptMethod
public String validateFormDataJS(String json) {
// Parse json, return error message if that's the case.
return JS_VALIDATOR_RESPONSE_OK;
}
Tamas