Tuesday, September 14, 2010

Apache Click 2.2.0: Dynamic Form Validation

In this three part series I'll blog about some of the new features added in 2.2.0, namely:

  1. DataProviders (Japanese version)
  2. Explicit binding (Japanese version)
  3. Dynamic Form Validation ( Japanese version)


    In this third and final installment I'll cover conditional form validation. This feature simplifies dynamic Form behavior by allowing developers to control when form validation should be applied. Generally form validation should be applied when the user submits the completed form. However when working with dynamic forms, the form is often submitted (using JavaScript) to the server in an incomplete state in order to add dynamic Fields and components. I'll refer to this as "incomplete submissions" because the form was not completely filled out by the user when the submission occurs.

    Ideally form validation should not be applied to incomplete submissions as the user will be presented any validation errors that occurred. Seeing the errors will be unexpected for the user since they are still busy filling out the form and did not intend to perform a submission.

    Let's look at a simple example. Below we have a page with a required name field and a checkbox that indicates whether the user is married or not. If the married checkbox is checked, a field is displayed for capturing the spouse's name. The spouse's name field is also required.

    public class ValidationDemo extends BorderPage {
    
        Form form = new Form("form");
    
        // The submit button is used to submit the completed form. Note, do not
        // name the Submit button "submit". That will cause a JavaScript error when
    // the button is clicked.
        Submit ok = new Submit("ok");
    
        @Override
        public void onInit() {
            super.onInit();
            form.add(new TextField("fullName", true));
            addControl(form);
            Checkbox married = new Checkbox("married", "Married?");
    
            // We use JavaScrcipt to submit the "incomplete" form when the user
            // clicks the checkbox.
            married.setAttribute("onclick", "form.submit();");
            form.add(married);
    
            form.add(ok);
    
            // Explicitly bind the checkbox
            ClickUtils.bind(married);
            if (married.isChecked()) {
                form.add(new TextField("spouseName", true));
            }
        }
    }

    Note the highlighted line above. We are using JavaScript to submit the form when the married checkbox is checked.

    Here is a screenshot of the page:

    When the checkbox is checked, the form is submitted and the spouse field is added. Next the form is validated and the required name and spouse error messages is displayed, as seen in the screenshot below:


    We don't want to show the validation messages yet. Particularly problematic is the error message for the Spouse Name field since the user has not seen this field until now.

    One solution is to clear the error messages if the form was submitted but the "ok" Submit button was not clicked, meaning the form was submitted through some other means ie. JavaScript. We can add this logic to the onPost page event:

    ...
    
    @Override
    public void onPost() {
        // Clear form errors if the "ok" submit button was not clicked
        if (!ok.isClicked()) {
            form.clearErrors();
        }
    } 

    This produces the screenshot below:

    This looks better. The Spouse Name has been added and no error messages are shown.

    However this solution is not ideal because the validation step is still performed and we have to manually clear the errors. So is there a better way?

    Indeed there is. By leveraging Explicit binding, one can conditionally switch off form and field validation as follows:

    if(!submit.isClicked()) { 
        form.setValidate(false); 
    }

    Let's revisit our earlier example and update it to use conditional validation:

    public class ValidationDemo extends BorderPage {
    
        Form form = new Form("form");
    
        // The submit button is used to submit the completed form. Note, do not
        // name the Submit button "submit". That will cause a JavaScript error when
    // the button is clicked.
        Submit ok = new Submit("ok");
    
        @Override
        public void onInit() {
            super.onInit();
            form.add(new TextField("fullName", true));
            addControl(form);
            Checkbox married = new Checkbox("married", "Married?");
    
            // We use JavaScript to submit the "incomplete" form when the user
            // clicks the checkbox.
            married.setAttribute("onclick", "form.submit();");
            form.add(married);
    
            form.add(ok);
    
            // Bind the submit button. If it wasn't clicked it means the Form was
            // submitted using JavaScript and we don't want to validate yet
            ClickUtils.bind(submit);
    
            // If the Form was submitted but the submit was not clicked, don't validate
            if(form.isFormSubmission() && !submit.isClicked()) {
                form.setValidate(false);
            }
    
            if (married.isChecked()) {
                form.add(new TextField("spouseName", true));
            }
        }
    }

    The screenshot below shows the form after checking the checkbox:



    As you can see, no errors are displayed since the form was not validated.

    Conditional form validation together with explicit binding provides a good combination for adding dynamic behavior to your Click pages and forms.

    This concludes the three part series covering the new features of Click 2.2.0.

    No comments: