Have you ever got into the situation where you feel like you need to stretch the limits of HTTP form processing?
Sometimes when developing complex web applications you end up with a form that has just too many features on it. This situation is particularly common when the application uses Ajax forms, as these forms often end up accumulating an enormous amount of funcionality - different actions for different buttons, events related to list choices or check box changes, partial screen updates etc.
One of the applications I'm currently working on has a requirement that the user should be able to upload files as part of bigger system interactions. The upload can happen in a couple of different screens and since I'm using Wicket, it feels natural to create a file upload component that can be reused across all the screens.
My first approach was to use to separate HTTP forms: one for the use case interaction and one for the file upload interaction:
<form wicket:id="mainForm" > (...main form fields) <input type="submit" wicket:id="mainSubmit" /> </form> <form wicket:id="uploadForm"> <input type=file wicket:id="upload" > ... <input type="submit" wicket:id="uploadSubmit" /> </form>
While this idea works , it has a major drawback: what if the user fills in the main form, chooses a file in the upload field (in the upload form) and hits the mainSubmit button? The main form will be submited but uploadForm will not and hence the file upload will not happen. This behaviour would be quite confusing for the end user, who would expect the file selected to be uploaded as part of the submit.
I could try to force the user to submit the uploadForm first - but the upload is an optional activity, so there is no way to know beforehand if the user will want to upload a file or not.
I could try to embed the upload field on the main form - but that gets complicated by the fact that the user must be able to upload multiple files before completing the action and I wanted a solution that I could reuse accross different screens with different forms.
I was about to make a radical change in the user interaction flow to work around this problem when I stumbled upon Wicket support for nested forms. Nested forms are a little known wicket feature that allows one to write forms like this:
<form wicket:id="mainForm" > (...main form fields) <form wicket:id="uploadForm"> <input type=file wicket:id="upload" > ... <input type="submit" wicket:id="uploadSubmit" /> </form> <input type="submit" wicket:id="mainSubmit" /> </form>
This is exactly what I wanted:
- If the user fills the main form, selects a file and presses the main submit button the main form contents and the selected file are submited.
On the other hand if the user hits the uploadSubmit button after selecting a file, this file is uploaded and the user can keep editing the main form contents and even upload another file in the same screen.
The Java implementations for the main form and upload form can be in different Java classes, and the upload form can be made generic enough so it can be reused in different screens.
You must be asking yourself: the HTML standard doesn't have support for nested forms, right? How comes Wicket allows you to do that?
The answer is Wicket magically replaces the inner form with a <span> section, so that there will be only one HTML form, but in the Java representation it still maps each form component to the right form.
When any submit button is clicked the content of all forms will be submited:
- If the submit button is in an inner form, Wicket will call only the inner form onSubmit event handler.
- If the outer form submit button is clicked Wicket will call both forms onSubmit handlers.
- The exact order in which events happen is documented here.
I don't know if other web frameworks also support this feature. I found it extremely useful but unfortunately not very well documented in Wicket. In any case, congratulations to the Wicket team - this feature implementation showcases the amazing amount of craftsmanship of the Wicket development team.