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?
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
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
still fuzzy after looking at that screenshot. could this be used for dynamically adding poll choices on poll form?
Well, the module
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
(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...
..., one of the issues you came across, use the #id attribute rather than the #attributes array. See an example in upload.module.
Thanks
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
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
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
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?
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?
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
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!
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
I was looking for this way of introducing information. Is it possible to do it without using this method?