templates.demos.htmlForms.multiple.html Maven / Gradle / Ivy
Show all versions of spincast-website Show documentation
{% extends "../htmlForms.html" %}
{% block demoSectionClasses %}demo_html_forms_multiple{% endblock %}
{% block meta_title %}Demos - HTML Forms - Multiple Fields{% endblock %}
{% block meta_description %}Multiple Fields HTML Forms with complex validation demo{% endblock %}
{% set demoId = "multiple" %}
{% block scripts %}
{% endblock %}
{% block demoBody %}
Multiple Fields / Complex validation
Please note :
-
When you're done playing with the form,
make sure you scroll to the How-To below the form, a quick tutorial will
show you how the demo works.
-
There is no client-side validation in this demo. It's on
purpose since we want
to demonstrate how server-side validation works and how the
Validation Messages
can be displayed.
-
The demo is fully functional even when javascript is disabled.
In this demo, we're going to look at more complex validation, where many different types of fields are validated,
and where the validity of some fields
depends on the value of other fields.
Enter any valid or invalid values in the following form and submit it to see
the validation in action!
You can also try the tests suggested on the right...
Code
-
The controller (the backend part) :
DemoHtmlFormsMultipleFieldsController.java
-
The
HTML
template (the frontend part) :
multiple.html
How to - Frontend
We already introduced the process of validating a form in the Introduction
demo. In this one, we focus mostly on how to display the various types of fields, how to validate a field by comparing its value with
another field, and how to run conditional validations.
Let's first examine how we render the fields in the
HTML
template, and how we use Validation Filters
for the validation messages
...
The "email"
and "emailAgain"
are simple text inputs.
Here's the "email"
field :
{% verbatim %}
<input type="text" class="form-control" id="email"
name="demoForm.email" placeholder="Email"
value="{{demoForm.email | default('')}}" />
{{validation['demoForm.email'] | validationMessages()}}
We use "{{demoForm.email | default('')}}"
to
fill the inital value [3] and "{{validation['demoForm.email'] | validationMessages()}}"
to display the potential Validation Messages
[4].
{% endverbatim %}
The tags
section is more interesting :
{% verbatim %}
<div class="col-sm-2 fieldGroupLabelAndMessages">
<label class="control-label">Tags *</label>
{{validation['demoForm.tags'] | validationGroupMessages()}}
</div>
<div class="col-sm-4">
<input type="text"
class="form-control {{validation['demoForm.tags[0]'] | validationClass()}}"
id="tag1" name="demoForm.tags[0]" placeholder="Tag 1"
value="{{demoForm.tags[0] | default('')}}" />
{{validation['demoForm.tags[0]'] | validationMessages()}}
<input type="text"
class="form-control {{validation['demoForm.tags[1]'] | validationClass()}}"
id="tag2" name="demoForm.tags[1]" placeholder="Tag 2"
value="{{demoForm.tags[1] | default('')}}" />
{{validation['demoForm.tags[1]'] | validationMessages()}}
<input type="text"
class="form-control {{validation['demoForm.tags[2]'] | validationClass()}}"
id="tag3" name="demoForm.tags[2]" placeholder="Tag 3"
value="{{demoForm.tags[2] | default('')}}" />
{{validation['demoForm.tags[2]'] | validationMessages()}}
</div>
{% endverbatim %}
Those fields form a group. We can know this by looking at their
"name"
attributes : "demoForm.tags[0]"
,
"demoForm.tags[1]"
and "demoForm.tags[2]"
. This syntax
indicates that those fields are part of the same group and have a specific position in it.
At the top of this code snippet, you can see that there is section which
describe the section those fields are in : "Tags *"
. We
use this zone to output the Validation Messages
associated with the
group itself :
{% verbatim %}
{{validation['demoForm.tags'] | validationGroupMessages()}}
{% endverbatim %}
If at least one of the "tag"
field is invalid, this filter will display an
error : "Some tags are invalid."
. This error is not associated
with a particular field, but with the group itself.
Next, we have the "Favorite drink"
radio buttons group.
In this section too we have
a zone where some Validation Messages
may be displayed for the group itself :
{% verbatim %}
<div class="col-sm-2 fieldGroupLabelAndMessages">
<label class="control-label">Favorite drink *</label>
{{validation['demoForm.drink'] | validationGroupMessages()}}
</div>
{% endverbatim %}
Otherwise, every radio button of the group is listed using a code similar to this :
{% verbatim %}
<div class="radio">
<label for="drink0">
<input type="radio"
id="drink0"
name="demoForm.drink"
{{demoForm.drink | checked("tea")}}
value="tea" /> Tea</label>
</div>
<div class="radio">
<label for="drink1">
<input type="radio"
id="drink1"
name="demoForm.drink"
{{demoForm.drink | checked("coffee")}}
value="coffee" /> Coffee</label>
</div>
//...
Explanation :
-
5 : All the radio buttons of this group
have the exact same
"name"
attribute! Not only does this
make the fields being a group in the eyes of the browser (only one radio button
of this group can be checked at a given time), but it also tells Spincast that those
radio buttons are part of the same group.
-
6 : We use the
checked(...)
filter to determine if a radio button must be checked or not. Learn
more about this filter in the Provided functions and filters
section.
{% endverbatim %}
The "Pick 2 numbers *"
section allows more than one option to be picked, so
checkboxes are used :
{% verbatim %}
<div class="checkbox">
<label for="num1">
<input type="checkbox"
id="num1"
name="demoForm.numbers[0]"
{{demoForm.numbers[0] | checked("1")}}
value="1" /> 1</label>
</div>
<div class="checkbox">
<label for="num2">
<input type="checkbox"
id="num2"
name="demoForm.numbers[1]"
{{demoForm.numbers[1] | checked("2")}}
value="2" /> 2</label>
</div>
//...
{% endverbatim %}
Those checkboxes are part of the same group, so we want to receive them as an array
when the form is submitted. For that reason,
it's recommended to use brakets ("[]"
) at the end of their "name"
attributes and, when possible, to specify the position of the element inside those brackets.
As for the "drink"
radio buttons fields,
we use the checked(...)
filter to determine if an option must be checked
or not.
Finally, the "Favourite music styles?"
and the "Action on submit"
are both <select>
fields. The first one, "musicStyles"
,
allow multiple options to be selected :
{% verbatim %}
<select multiple id="musicStyles" name="demoForm.musicStyles[]"
class="form-control">
<option value="rock" {{demoForm.musicStyles | selected("rock")}}>Rock</option>
<option value="pop" {{demoForm.musicStyles | selected("pop")}}>Pop</option>
<option value="jazz" {{demoForm.musicStyles | selected("jazz")}}>Jazz</option>
<option value="metal" {{demoForm.musicStyles | selected("metal")}}>Metal</option>
<option value="classical" {{demoForm.musicStyles | selected("classical")}}>Classical</option>
</select>
{% endverbatim %}
Since more than one option can be selected, we make the "name"
attribute of this
field end with "[]"
. That way, the selected options are always going to be grouped
together as an array when the form is submitted.
Note that with <select>
fields, we use the selected(...)
filter to determine if an option must be selected or not. This filter is going to output a "selected"
attribute, where required.
The last thing on the frontend we'll going to have a look at is how to display the
status of the form itself. Notice that we display a message at the top of the form to
show if it is valid or not :
{% verbatim %}
{% if validation['demoForm._'] | validationSubmitted()%}
{% if validation['demoForm._'] | validationHasErrors() %}
{% set alertClass = "alert-danger" %}
{% set status = "contains errors" %}
{% elseif validation['demoForm._'] | validationHasWarnings() %}
{% set alertClass = "alert-warning" %}
{% set status = "contains warnings" %}
{% elseif validation['demoForm._'] | validationIsValid() %}
{% set alertClass = "alert-success" %}
{% set status = "is valid" %}
{% endif %}
<div class="row">
<div class="col-sm-6">
<div class="alert {{alertClass}}">
<img src="/public/images/icons/info2.png" /> The Form <strong>{{status}}</strong>
</div>
</div>
</div>
{% endif %}
Explanation :
-
2 : This line validates that the form
has been submitted. We don't want to display any message when the form is
displayed for the first time! The
"_"
element is a special element which
represents a Validation Set
itself.
-
We use the
validationHasErrors()
filter [4],
the validationHasWarnings()
filter [8] and
the validationIsValid()
filter [12] to
pick the appropriate CSS
class and status to use for the message we're going to
display.
-
20 : We output the selected
CSS
class.
-
21 : We output the selected status.
{% endverbatim %}
How to - Backend
On the backend, this demo shows how to run a validation or not depending on the result of a previous
validation. For example :
String email = form.getString("email");
if (StringUtils.isBlank(email)) {
form.addError("email",
"email_empty",
"The email is required");
}
if (form.isValid("email") && !form.validators().isEmailValid(email)) {
form.addError("email",
"email_invalid",
"The email is invalid");
}
As you can see, we only perform the second validation if the first one is a success! This is often
very useful since we do not want to display multiple error messages for a single field when, in fact,
the field was simply left empty.
In this demo, we also see how to validate a field by comparing it's value to the value of another field.
For example :
String email = form.getString("email");
// ...
String emailAgain = form.getString("emailAgain");
// ...
if (form.isValid("emailAgain") && !emailAgain.equals(email)) {
form.addError("emailAgain",
"emailAgain_mustMatch",
"Must match the first email field.");
}
There are more validations performed in this demo! We suggest you have a look
at DemoHtmlFormsMultipleFieldsController
code to see all of them.
More info
Make sure you also try the first demo of this section, Introduction - Single field
which introduces forms and validation using Spincast.
Otherwise, you can learn everything about forms and validation in the dedicated
Forms section of the documentation.
{% endblock %}