{% 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 :

Here, 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 inputs in the following form and submit it to see the validations in action!

You can also try the tests suggested on the right...

{% if validation._ | validationSubmitted()%} {% if validation._ | validationHasErrors() %} {% set alertClass = "alert-danger" %} {% set status = "contains errors" %} {% elseif validation._ | validationHasWarnings() %} {% set alertClass = "alert-warning" %} {% set status = "contains warnings" %} {% elseif validation._ | validationIsValid() %} {% set alertClass = "alert-success" %} {% set status = "is valid" %} {% endif %}
The Form {{status}}
{% endif %}
{{validation['demoForm.email'] | validationMessages()}}
{{validation['demoForm.emailAgain'] | validationMessages()}}
Validations to test :
{{validation['demoForm.tags'] | validationGroupMessages()}}
{{validation['demoForm.tags[0]'] | validationMessages()}} {{validation['demoForm.tags[1]'] | validationMessages()}} {{validation['demoForm.tags[2]'] | validationMessages()}}
Validations to test :
  • This is a group of fields : they all have the same "name" attribute. Because of this, when the form is submitted those fields are retrived together as an array on the server.

    The three tags are required.

    Fill all tags
{{validation['demoForm.drink'] | validationGroupMessages()}}
Validations to test :
  • A favorite drink is required.

    Choosing "beer" is invalid if the "Action on submit" field is set to "Warning choice!"... Try it!

{{validation['demoForm.numbers'] | validationGroupMessages()}}
Validations to test :
  • You have to pick exactly two numbers and both must be odd or both be even!

{{validation['demoForm.musicStyles'] | validationGroupMessages()}}
Validations to test :
  • There is no validation on this field, but you can check that if you select multiple of its options (using the CTRL/Command key), they will still be selected when the form is redisplayed.

{{validation['demoForm.action'] | validationMessages()}}

{{validation['demoForm.acceptTos'] | validationMessages()}}
Validations to test :
  • Choosing the "Warning choice" will result in a Warning Validation Message. If you choose this option, and if all the other fields are valid, you will see that the form is going to be processed and you are going to be redirected.

    This is to demonstrate that Warning Validation Messages don't make a form invalid, only Error Validation Messages do!

Code

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 their 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 (learn more).

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. You can learn more about this in the Predefine Validation options section.

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 :

{% 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. Make sure you read the Getting the submitted form data sectiton to learn why.

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._ | validationSubmitted()%}

    {% if validation._ | validationHasErrors() %}
        {% set alertClass = "alert-danger" %}
        {% set status = "contains errors" %}
        
    {% elseif validation._ | validationHasWarnings() %}
        {% set alertClass = "alert-warning" %}
        {% set status = "contains warnings" %}
        
    {% elseif validation._ | 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 :

{% endverbatim %}

How to - Backend

On the backend, this demo first shows how to run a validation or not depending on the result of a previous validation. For example :

 ValidationSet lastResult =
         validationSet.validationNotBlank().jsonPath("email").failMessageText("Please enter the email").validate();

 if(lastResult.isValid()) {
     validationSet.validationEmail().jsonPath("email").failMessageText("Please enter a valid email").validate();
 }

As you can see, we only perform the second validation if the first one is valid! This is often very useful since we do not want to display 5 different 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 :


lastResult = validationSet.validationEquivalent(form.getString("email"))
                          .jsonPath("emailAgain")
                          .failMessageText("Must match the first email field.")
                          .validate();

In this snippet, we retrieve the "email" element from the form object using form.getString("email"). We then compare this value to the "emailAgain" validated element using the validationEquivalent(...) predefined validation. If they are not equivalent (which is a loose version of an equality check), an Error Validation Message is recorded for the validated "emailAgain" field. You can test this by yourself by entering two valid but different emails in the demo form!

Another interesting validation in this demo is :

if("beer".equals(form.getString("drink")) && validationSet.isWarning("action")) {
    validationSet.addError("drink", 
                           "BEER_AND_WARNING_ACTION", 
                           "'Beer' is invalid if the 'Action on submit' field is a Warning!");
}

This custom validation doesn't use any Predefined Validation and the resulting Error Validation Message, when the validation fails, is added manually to the Validation Set, using the "drink" validation key.

There are more validations performed in this demo! We suggest you have a look at DemoHtmlFormsMultipleFieldsController code to see them all.

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 those topics in the Validation and Forms sections of the documentation.

{% endblock %}