Stepping up to the plate
Back to our story of Ajax vs the Drupal Form API. After modifying poll.module to work with ahah_forms in a couple of hours, I was feeling cocky, and decided to go for the gold ring: Views. I use views
and think it is one of the key drupal modules. And it is the perfect target for Ajax, because creating a View can take over a dozen page refreshes. I went in optimistic. If you read my first post on this topic (
Update: I have just released version 1.3 of ahah_forms, which includes a significantly simplified version of views_ui.module-example. I realized that since I was not able to accomplish my goal of per-section form processing, I can limit most of my changes to the theming functions. Most of the discussion below is still valid.
The Four Steps
I had abstracted four steps to working with a dynamic subform:
- Pull existing info about the subform from the $node (which comes from the db), or from the $_POST, into an intermediate data structure.
- Process any commands (add/edit/delete) in the $_POST, updating the data structure
- Convert the data structure into a form array
- Add the subform controls to the form array
It turns out there are quite a few unspoken assumptions here that make this work smoothly for Ajax. Views_ui did all those step, more or less, but not in a way I could easily work with. So here are some rules of thumb for easier Ajaxing:
- Do all of these steps for each subform before moving on to the next. Views_ui tends to do a single step for all the subforms before moving on to the next step
- Do all of these steps in the form creation step of the formAPI. Views_ui does some of the steps in the theme stage.
- Have descrete theming functions for each subform, and use #theme to apply them.
- It's easier to cheat and access parameters in $_POST, than to do things the correct formAPI dynamic form way, which is to rebuild the form, apply the $_POST to the form with drupal_process_form (happen automatically if you use drupal_get_form), and then access the form values. Ugh. This one will probably come back to bite me, and hopefully is just be my current stage of formAPI ignorence, but it feels brittle when it comes to fragments.
- Avoid logic in your theme stage as much as possible.
Once I figured out all of those issues, I just needed to:
- Add #ahah_binding to the subforms and interesting buttons
- Sprinkle in wrappers for all the subforms (note: fieldsets make crappy wrappers, b/c you can't regenerate the legends).
- Make sure all of the interface elements had appropriate classes or ids.
- Add a menu item and function for updating and building a subform
- And rewrite my ahah_forms.js to return more info about the element clicked (again to accomidate formAPI processing, more about this in my next post).
The Nitty Gritty
For those of you who want more details, here is an overview of the changes I made along with the names of the involved functions (function_x means I added a set of functions, one for each subform. ei: views_ui_edit_form_field, views_ui_edit_form_argument, etc)
Broke views_edit_view (main form builder function), up into
multiple smaller per-section form builder functions. Small form builder
functions set #theme for each section form [views_edit_view, views_ui_edit_form_x & views_ui_edit_form_section]
- Removed _views_check_ops, and spread it's functionality into the per-section form builders [_views_check_ops]
- Broke theme_views_edit_view up likewise. Pulled sections in with druapl_render($form[ section ]); [theme_views_ui_edit_x_section, theme_views_edit_view, views_ui_render_section]
- Added wrappers in theme_views_edit_view (had to add div, fieldsets don't make good wrapper, since legend gets erased) [theme_views_edit_view] .
- Added #ahah_bindings to add button and views_image_buttons to each section, and to expose filter in filter section [views_ui_edit_form_section, views_ui_add_add_button, views_ui_edit_form_filters]
- Added class attributes and section name to the generated buttons, so I can do step 4 [views_ui_add_button, views_ui_add_buttons, theme_views_imagebutton, views_ui_add_filter].
Again, if you'd like to try it out for yourself, go to