Conditional Validation On Submitting Form Component (Non-Nested Form Approach)
Say we have four form components within the same form:
- name A
- description A
- name B
- description B
Sometimes we want the whole form to validate/process (Button C), but sometimes we only want "A" form components to validate/process (Button A). One way to accomplish this is to:
final TextField nameA = new TextField("name", ...); final TextField descriptionA = new TextField("descriptionA", ...); final TextField nameB = new TextField("nameB", ...); final TextField descriptionB = new TextField("descriptionB", ...); // the validators can be anything, but for simplicity we just use required nameA.setRequired(true); descriptionA.setRequired(true); nameB.setRequired(true); descriptionB.setRequired(true); final Button buttonA = new Button(id) { public boolean onSubmit() { // because we overrode the form processing we need to handle validation/processing on the components ourselves nameA.validate(); descriptionA.validate(); if(!nameA.isValid() || !descriptionA.isValid()){ // didn't validate so we stop processing (validation errors will be displayed provided we are using a FeedbackPanel or similar) return; } // TODO : now we have the updated values/models so we can perform whatever button A is supposed to do } }); // set the form processing to false so that no validation/processing will occur on the form when button A is clicked buttonA.setDefaultFormProcessing(false); final Button buttonC = new Button(id) { public boolean onSubmit() { // TODO : all validation passed because form processing is true so we can perform whatever button B is supposed to do } });
Conditional Validation On Each Form Component (Nested Form Approach)
Flagging a form field as "required" is the most common kind of validation. In most cases, this can be specified statically as follows:
add(new TextField("foo").setRequired(true));
In some cases, however, whether or not the field is required cannot be determined when the form is created. Like many properties in Wicket, we can override the property setter (isRequired
, in this case) to defer the evaluation of the property until the last possible moment:
add(new TextField("foo") { public boolean isRequired() { // return true if the field is required } }
That's the simple part. Unfortunately, implementing isRequired
can be tricky, since it's called very early in the form processing cycle, before values are converted and models are updated. Below we provide a few recipes that cover some common scenarios.
Submit Button
In this recipe, the field is in a form with multiple submit buttons. We only want to require the field when one of the buttons is clicked:
Button submit = new Button("submit") { public void onSubmit() { // handle form submission } } form.add(submit); TextField foo = new TextField("foo") { public boolean isRequired() { Form form = (Form) findParent(Form.class); return form.getRootForm().findSubmittingButton() == submitButton; } } form.add(foo);
Note the call to getRootForm
. Technically, this is only required in nested forms.
If you would like to bypass validation altogether you can do so by:
new Button("submit").setDefaultFormProcessing(false);
CheckBox
In this recipe, the field is only required when a checkbox on the form is checked:
final CheckBox checkBox = new CheckBox("cb"); form.add(checkBox); TextField foo = new TextField("foo") { public boolean isRequired() { return Strings.isTrue(checkBox.getValue()); } } form.add(foo);
Radio Button
Here the field is only required when a particular radio button on the form is selected:
final RadioGroup radioGroup = new RadioGroup("radioGroup"); form.add(radioGroup); final Radio radio1 = new Radio("radio1"); radioGroup.add(radio1); TextField foo = new TextField("foo") { public boolean isRequired() { return Strings.isEqual(radioGroup.getInput(), radio1.getValue()); } } form.add(foo);
DropDownChoice
Normally, you give a list of domain objects to a DropDownChoice. We can check which one was selected with the getConvertedInput()
method as follows:
public class DocumentType { private String name; private boolean hasExpiryDate; // getters and setters omitted } List<DocumentType> allDocumentTypes = ...; final DropDownChoice ddc = new DropDownChoice("documentType", allDocumentTypes, new ChoiceRenderer("name")); form.add(ddc); TextField expiryDate = new TextField("expiryDate", Date.class) { public boolean isRequired() { DocumentType dt = (DocumentType) ddc.getConvertedInput(); return dt != null && dt.getHasExpiryDate(); } } form.add(expiryDate);