Subform support
Affects | Status | Importance | Assigned to | Milestone | |
---|---|---|---|---|---|
Anewt |
New
|
Undecided
|
Unassigned |
Bug Description
It is not possible to have several forms with just one submit button. Normally you wouldn't need this, but there are situations where this would be very useful.
One such situation is this: Suppose you have some element with all kinds of annotations, and all those annotations can be in multiple languages. And you want to have one form with the element plus all those annotations in all languages. You could make one form and manually add controls for all languages and give them unique and meaningful id's, and during processing you reconstruct the elements from the id's. But this is very cumbersome. It would be much better to create one form for each language, and have all forms combined into a single form with one submit button.
One way is modifying the renderer so that it doesn't render the actual <form> element, then render all the forms and manually enclose them with a <form> element. But you still have the problem that the names should be unique in the whole form node, and the id's should even be unique in the whole document.
I've worked on this problem and I will submit the results momentarily.
My solution is to modify AnewtForm so that it has a 'prefix' element, which is actually an array (as to include nested subforms). If you have a form with the prefix array('foo', 'bar'), and a control named 'foobar', then during the rendering it will have the rendered name 'foo[bar][foobar]' and the id 'foo-bar-foobar'.
So controls have a 'name', which is just the name as normal which is unique in the form. They also have a 'rendered-name', which is the name prefixed by the form prefix as described above. All controls had to be modified to use this rendered-name.
A new control AnewtFormContro lSubform is created, which takes an AnewtForm in its constructor. It also needs a renderer some time before rendering (optional constructor argument). It will set the prefix of the subform by its own prefix plus its own name. But because its own prefix is only known when the enclosing form has received its prefix, this is done as late as possible (during rendering, see open issues below).
The AnewtFormRenderer was modified so that hidden controls can be rendered separately. AnewtFormRender erDefault was modified so that all controls can be rendered without an enclosing <form> node. During rendering, the AnewtFormContro lSubform will set the prefixes and render the hidden controls and the rest of the controls.
During form submission AnewtForm: :autofill( ) will strip any custom prefixes from the $_GET or $_POST values and fill the form with the rest. AnewtFormContro lSubform: :set_value( $value) will pass the values along to its subform and AnewtFormContro lSubform: :get_value( ) will return the form values of the subform. Normal fill handling will take care of the rest.
Validation (is_valid()) will also chain as expected to the subforms.
Processing (handle_ (in)valid( )) of forms is *not* chained automatically. AnewtFormContro lSubform has a method 'process()' which automatically calls 'process' in the subform. This has to be called manually from for example the handle_valid() from the enclosing form (note that process() in the subform will automatically call handle_valid() or handle_invalid() depending on its validity).
Open issues:
Complex controls with multiple input tags are not yet supported in subforms. The only complex control in anewt itself is the AnewtFormContro lFileUpload. The main problem here is that it creates extra controls postfixed with an attribute (like "-remove"), but that will create a name like "foo[bar] [foobar] -remove" , which php does not process. The simple solution (which also avoids name conflicts) will be to change them so that postfixed like "[remove]", but then the original input element must also be postfixed by something.
The prefix for the AnewtForm is set with AnewtFormContro lSubform- >set_prefix( ), which is called during AnewtFormContro lSubForm- >build_ widget( ). So before rendering, the prefixes will not be correct unless you manually call set_prefix().
Just realized but not tested: A subform containing only controls which may not be submitted (like checkboxes and multi-select controls) will probably fail on filling when nothing is submitted (i.e.: all the checkboxes are off). Simple workaround for now is to ignore the value of...