In Drupal 6, FAPI 3 is finally getting to the point where it's powerful enough to do some of the things that it's been a real bear to do with it. For one, in Drupal 6 it now has image buttons, and using form alter you can now directly replace a regular submit button with an image button pretty easily. And submit buttons have their own submit and validate functions attached, so it's now very easy to have different buttons operate completely independently. This is a major improvement.
It's still a little bit difficult to get just part of a form; it's also very inefficient to get just part of a form, since the 'right' way is to build the whole form, and then render what you want.
But there are still some deficiencies in the system that we are going to need to address.
The first problem I want to address is that the design of the form system actually mixes logic and presentation. Yes, it's true that it seems like it doesn't. You've got the $form array, and theme functions. But for the most part, the forms api tries to do all of the rendering work under the hood and let it render itself. And it provides some constructs to make this 'easier'. One such example are fieldsets.
The problem I've run into here is that we have created mixed metaphors for how form data is nested. Let me give a completely frivolous example:
<?php
// This widget is creating a fieldset; everything nested under the fieldset
// will be displayed that way.
$form['fieldset'] = array
'#type' => 'fieldset',
);
// This widget creates an object; everything within this object will be
// grouped together in the 'values' construct, because of the #tree => TRUE.
$form['fieldset']['object1'] = array(
'#tree' => TRUE,
);
// This is a textfield that belongs to object and will be displayed in
// the fieldset
$form['fieldset']['object1']['foo'] = array
'#type' => 'textfield',
'#title' => t('Foo'),
...
);
// This widget creates another object.
$form['fieldset']['object2'] = array(
'#tree' => TRUE,
);
// This is a textfield that belongs to object2 and will be displayed
// in the fieldset
$form['fieldset']['object2']['bar'] = array
'#type' => 'textfield',
'#title' => t('Bar'),
...
);
?>While this example is crude, it shows the very tip of the iceberg of the problem; that is, we're using the container arrays to control both presentational grouping and data grouping. This turns out to be bad because of how #tree works; tree is basically either ON or OFF. Once you get into data groupings, it becomes difficult to also use this in presentational groupings -- this is something I've found in Panels where my forms get very deep, and there are a lot of different forms available because everything is pluggable and provides its own form snippets.
Another side effect of this problem is that it becomes very difficult for a themer to really do much with this form. The actual rendering here is so deep in the system that, even when someone understands Form API (something that is difficult in part because of the difficulty of documentation), it becomes quite difficult to figure how to do something simple like move object2 into a different fieldset.
Also, Form API has 'weights' but because those weights are completely guessed, it's really difficult to actually insert something somewhere in the middle. You can, if you prod and probe and guess at what the weight should be, but really what is likely to happen is that form_alter will add stuff to the beginning or to the end of a form (or to a fieldset within a form).
With all the work I've been doing on the theming system lately, I never did manage to address forms. And I'm finding that the forms are coming up very short because of this mixture. While it's easier on the form developer to use the form array and have fieldsets appear in the form, more and more I'm thinking that this is a poor methodology and that the fieldsets should be added exclusively by the theming layer and instead we should use the container or some other marker as a guide as to where this should be placed. That would mean that every form created would need to have a dedicated theme function.
But is this really bad? I imagine that a large percentage of Drupal developers who are comfortable with Forms API who just read that went "NOOOOOO!" at the suggestion that they need to do more work on their forms. However, I counter that with the way the theming system works, it is a necessary evil because by creating dedicated themes, you can improve the look, feel and usability of any given form and give themers more to work with in changing the look and feel of the form to suit their site.
We need to evolve form api to the point where it is easy for the site designer to mold a form to suit that site. If you look at existing Drupal sites, forms are generally the one part that consistently point to a site being Drupal. (That and tabs, but that's another story; one that also deserves a rant).
Additionally, it can be really hard to address the classes and actual structure. Especially when using something like 'checkboxes' which embeds a bunch of form-items but doesn't actually currently provide adequate classes to easily identify a particular part of the checkboxes, unfortunately. With $form['#attributes'] you can add classes...but wait, isn't that presentation again? Oh dear.
The problem, of course, is finding the balance. There's a lot of icky HTML in forms that we would like to avoid making developers actually deal with. Some of what we need to do, I think, is easy: Remove '#type' => 'fieldset' and move that exclusively to the theme layer, for example, but make sure we keep some way of giving cues, so that form_alter can continue to place stuff within the obvious areas.
But what do we do about checkboxes? How do we make it easier for a themer to reformat a list of checkboxes so they appear side by side, or in 3 columns? I can do that right now, but it's kind if difficult, and it requires more than just theming the form. How do we make it easy for a themer to change how the labels are positioned? Sometimes you really don't want the labels to appear over the form item. And that ':' that appears on form items? I've seen a lot of commentary about how to deal with that. It's one of those things that really should not be as difficult as it is.
Perhaps a bunch of it can and should be dealt with using '#' directives that theme('form_item') uses to make some decisions. Some of them are very difficult philosophical decisions.

Themable forms
I definitely like the idea of forms that can themed. I definitely spend too much time in hook_form_alter() making trivial changes that should be done at the theming layer.
And it seems like there should be a way of having a generic theme_form() that developers can rely on for simple forms, not having to create a theme function for each and every form.
But overall, the current shortcomings with formsapi are trivial when compared with the benefits it brings.
Food for thought
I think I agree -- it's a hard pill to swallow, as it would entail some big changes to how we handle certain aspects of form building, but there would definitely be a payoff. I'd love to brainstorm sometime, as I just haven't been able to come up with any starting-point ideas for how this could be executed well...
Also Labels
Another thing to consider is where labels get created and translated. A label should certainly be in the theme layer, but instead we inject a translated label into the form array when the form is built. Yes, there is a theme for those elements, but you would have to re-implement theme_textfield in such a way that you can figure out exactly which textfield it is in which form so you can do something intelligent with the label. I have started creating custom themes for individual element labels so that themers can do something with them without using code to figure out which form element they're dealing with, but I don't know if that is the best solution.
Fallbacks
As Harry Slaughter said, there should still be some sort of sane fallback so that form authors can crank out a functional form without having to delve deep into theme-land. As you say, those are different skillsets and forcing form authors to muck about in theme system is no better than forcing themers to muck about in the form system. I'm all for themers being able to redesign a form without bugging me, though. :-)
Karen also points out an issue with the theme system. In most cases it's very hard to get a sense of context to the theme function; if you want theme('foo') to look one way in the body of the page but a different way in a block, there's no un-yucky solution right now. I'm not sure of the solution there, either, but it's something to keep in mind.
Perhaps the inside-out rendering array that Eaton and others have been pondering since Sunnyvale could be useful?
CAPTCHAs
One thing to keep in mind is that sometimes you want modules to extend forms without having to manually update your theme functions. For example, the CAPTCHA module might want to CAPTCHA-enable a couple dozen forms. ;)
#parents
Most of this can be addressed with #parents. #parents represent the data structure while the array indexes are used for presentation. Yes, #tree can control how #parents are autogenerated but noone is stoping you from defining your own #parents.
Parents
Do you have a reference for an article that would explain #parents in a bit more detail?
Thanks,
John
reference for you
Hi John. If you try the following address http://drupal.org/node/48643 that should help clarify things a bit for you.
Regards
Bob
Re:Some current problems with Form API
As Harry Slaughter said, there should still be some sort of sane fallback so that form authors can crank out a functional form without having to delve deep into theme-land. As you say, those are different skillsets and forcing form authors to muck about in theme system is no better than forcing themers to muck about in the form system. I'm all for themers being able to redesign a form without bugging me, though. :-)
Karen also points out an issue with the theme system. In most cases it's very hard to get a sense of context to the theme function; if you want theme('foo') to look one way in the body of the page but a different way in a block, there's no un-yucky solution right now. I'm not sure of the solution there, either, but it's something to keep in mind.
Perhaps the inside-out rendering array that Eaton and others have been pondering since Sunnyvale could be useful?
Post new comment