Tutorial: A dynamically expanding AJAX/AHAH Drupal form

We encourage users to post events happening in the community to the community events group on https://www.drupal.org.
starbow's picture

I have spent the last couple of weeks trying to figure out how to cleanly do dynamic forms with incremental page updates. I have put together an example module which is available for download. I have also put together a tutorial that explains my technique and the issues I ran into. I am interested in hear what people think of my approach.

http://www.starbowconsulting.com/blog/tao/dynamically-expanding-ajax-aha...

Goal
To create a form widget that consists of an unlimited number of subwidgets, expands without needed a full page refresh, and can be easily added multiple times into any Drupal form.

Plan
Use hook_element to define the element, hook_nodeapi to handle the db interactions, and jQuery-based AHAH code to do the incremental page updates (AHAH – Asynchronous HTTP And HTML , not AJAX b/c there is not XML involved, and people seem to think AJAH isn’t sexy enough).

Comments

Demo?

jasonwhat's picture

This seems very cool, but without a sample it hard to understand exactly what it does. Do you have an example up to test out?

Screenshot

starbow's picture

I totally understand wanting a demo - that's something I always look for in modules I want to test out. But this module is a proof-of-concept and would need some going over before I would be comfortable running it on a publicly accessible system. So I have attached a screenshot to give folk an idea of what it does.

still not so clear

moshe weitzman's picture

still fuzzy after looking at that screenshot. could this be used for dynamically adding poll choices on poll form?

Well, the module

starbow's picture

Well, the module demonstrates all the techniques you would need to add dynamic poll choices, but it's definitely not something you can just plugin. I found the whole process took three distinct passes. The first to build a simple non-dynamic form; the second is to refactor it to have dynamic form building with full page submits (like the views UI is now), and the last to put in the javascript and incremental page update. I was hoping the last step would just involve writing javascript and adding a couple of functions, but it takes a fair amount of revising of the existing functions. Mostly breaking the functions up into smaller chunks, getting the wrapper in just the right place, and putting in useful classes and ids.
This module is demonstrating how to create a field type that can be attached to lots of different node type, and there is a fair amount of complexity to the module to support adding itself to the same node multiple times. Updating poll would probably be much simpler, but require scooping out parts of this module and sprinkling them into the poll code.

Wow

starbow's picture

(Note: this should go below the next comment :)

Wow, I just had a serious epiphany about how all the javascript to run something like this could be generated. I hacked out some code, and it looks good right now, but it is 3am so I will need to see if it makes any sense in the morning. If I am write, you could declare an element like:

<?php
$element
['foo'] = array(
   
'#type' => 'button',
   
'#bind' => array (
         
'event' => 'click',
        
'path' => 'widget/widget_update_js',
       
'wrapper' => 'widget_wrapper_widget1',
         
'params' => array( 'widget_name_js' => 'widget1' ),
    ),
);
?>

not have to write a single line of javascript or jquery, and still get the dynamic AHAH updates. Very exciting....although it will need some tweaking to handle attach itself to form elements that don't exist when the page is first generated...I don't think you can modify the javascript itself on an Ajax call. Maybe stubs for elements that you know will be added it...right more thinking in the morning.

To override the standard element id in forms api...

nedjo's picture

..., one of the issues you came across, use the #id attribute rather than the #attributes array. See an example in upload.module.

Thanks

starbow's picture

That makes total sense. I have been a little wary about using the properties marked as INTERNAL. I added a note about this to the "Converting 4.7.x modules to 5.x" page. Do you think the FAPI ref page should add the #id property to the Special Elements table?

Problem with rendering form object

aina's picture

Hi!

I use AHAH in my module and my goal is to render a dropdown after a change of the first.

I use the drupal_render API. Is this the right function to do that?
......................
for($i=1;$i<=10;$i++)
{
$tab[$i]=$i;
}

$form['select1'] = array(
'#id' => 'class-box',
'#type' => 'select',
'#title' => t('select1'),
'#options' => $tab,
'#description' => t('Please choose an option.'),
'#ahah_bindings' => array (
array(
'event' => 'change',
'path' => 'code/simple.php',
'wrapper' => 'target'
)) ,
);

$form['select2'] = array(
'#type' => 'select',
'#title' => t('select2'),
'#options' => $tab,
'#prefix' => '

',
'#suffix' => '

',
);
..................

In simple.php
for($i=1;$i<=2;$i++)
{
$tab2[$i]=$i;
}

$form['select2'] = array(
'#type' => 'select',
'#title' => t('select2'),
'#options' => $tab2,
'#prefix' => '

',
'#suffix' => '

',
);

drupal_render($form);

Thanks,

Aina

Ahah Forms has changed a lot

starbow's picture

Ahah Forms has changed a lot in the last two month. In particular, the new release introduces some functions that allow you to do dynamic subforms securely. I am doing some attack modeling right now, and will be doing a big announcement soon. But the big thing is that you should now take this example with a grain of salt. Please download the latest ahah_forms version and look at the todos.module for an example of current recommended practice.

Which is a long way to go to say, yes, drupal_render() is the right function to use there :)

cheers,
-tao

really interesting work. I

fago's picture

really interesting work.

I think the best way to use this would be in conjunction with subforms and #multipart forms.
So one could split up the form into several subforms, which could be rebuilt independently from the other form elements using usual #multipart programming techniques.
As you have already some subform code, are you already going into this direction?

#mutlipart?

starbow's picture

Can you tell me more about #multipart? I don't see it anywhere in the code base. Is it a property you invented for your embedded forms work?

My current thought with my dynamic subforms stuff is that it is good enough for the moment. Since the formAPI is going to be going through another massive change for D6, I am not going to invest a lot more time in it until I know the lay of the land.

#multistep?

jjeff's picture

Are you thinking of #multistep? More info and examples of #multistep can be found here:

http://www.lullabot.com/articles/drupal_5_making_forms_that_display_thei...

-= Jeff Robbins | Lullabot | Drupalize.me =-

Right

starbow's picture

right, he probably does mean #multistep, which I now know way too much about! Don't get me started. Most of the motivation of dynamic_subform is about avoiding using #mutlistep.

LOL!

jjeff's picture

Yes. It seems like one of the (many) "dark corners" of the form API. Best taken in small doses and avoided when possible.

-= Jeff Robbins | Lullabot | Drupalize.me =-

subwidget

urwen-gdo's picture

I was looking for this way of introducing information. Is it possible to do it without using this method?