AJAX in Drupal Forms? - drupal

How would you go about constructing a step by step form that uses AJAX through Drupal to pull the next form step?
For example,
Step 1:
I like Baseball
I don't like Baseball.
When that person clicks on either Like or Don't Like, I want to use AJAX to recognize and pull the next part of the form, remove/hide the first section since its not needed, and present the next section.
Example:
Step 1:
I like Baseball
*click
(fade out)
Step 2:
My favorite team is __________
The player I like most is __________
What is the best way to do this through Drupal Form API? I know how to build the forms and modules, but I have never used AJAX yet. I know a few things exist out there that are supposed to help, but I wanted to know if anyone here has done it and how they approached it.

You may want to give a look at the AHAH helper module.

usually i am create full form with fieldsets, then control them manually by jquery.
i assume there lot of ready to go modules in drupal, some of these:
http://drupal.org/project/conditional_fields / http://drupal.org/project/multistep
also: http://www.google.ru/search?q=drupal+multistep+ajax+form

If you don't want to write any code, and don't need the entered data to be drupal nodes, I suggest using the webform module. It has a pretty simple UI for building forms, and allows you do do multipage forms with conditional fields. You can then export the results as CSV, email them, etc.

I have made to solutions for this problem in drupal 7. First one I solve it with Ajax as was requested(if someone want I can convert this to drupal6), however it should be better to solve this using attribute #states. So also made a solution in the bottom using states.
How to solve this using Ajax:
function ajax_in_drupal_form($form, &$form_state)
{
$baseball = array(
'like' => t('I like Baseball'),
'unlike' => t('I don\'t like Baseball')
);
$form['step'] = array(
'#prefix' => '<div id="baseball-wrapper">',
'#suffix' => '</div>',
);
if ($form_state['values']['baseball'] == 'like') {
$form['step']['team'] = array(
'#type' => 'textfield',
'#title' => t('My favorite team is'),
);
$form['step']['player'] = array(
'#type' => 'textfield',
'#title' => t('The player I like most is'),
);
}
else if ($form_state['values']['baseball'] == 'unlike') {
$form['step']['other'] = array(
'#type' => 'textfield',
'#title' => t('What do you like'),
);
}
else {
$form['step']['baseball'] = array(
'#type' => 'radios',
'#options' => $baseball,
'#title' => t('Select your option'),
'#ajax' => array(
'callback' => 'ajax_update_step_callback',
'wrapper' => 'baseball-wrapper',
),
);
}
return $form;
}
function ajax_update_step_callback($form, $form_state) {
return $form['step'];
}
Here is the solution using #states(The preferred way of solving it):
function states_in_drupal_form($form, &$form_state)
{
$baseball = array(
'like' => t('I like Baseball'),
'unlike' => t('I don\'t like Baseball')
);
// step 1
$form['step']['baseball'] = array(
'#type' => 'radios',
'#options' => $baseball,
'#title' => t('Select your option'),
'#states' => array(
'invisible' => array(':input[name="baseball"]' => array('checked' => TRUE),
),
)
);
// step 2 like baseball
$form['step']['team'] = array(
'#type' => 'textfield',
'#title' => t('My favorite team is'),
'#states' => array(
'visible' => array(':input[name="baseball"]' => array('checked' => TRUE)),
'visible' => array(':input[name="baseball"]' => array('value' => 'like')),
)
);
$form['step']['player'] = array(
'#type' => 'textfield',
'#title' => t('The player I like most is'),
'#states' => array(
'visible' => array(':input[name="baseball"]' => array('checked' => TRUE)),
'visible' => array(':input[name="baseball"]' => array('value' => 'like')),
)
);
// step 2 I don't like baseball
$form['step']['other'] = array(
'#type' => 'textfield',
'#title' => t('What do you like'),
'#states' => array(
'visible' => array(':input[name="baseball"]' => array('checked' => TRUE)),
'visible' => array(':input[name="baseball"]' => array('value' => 'unlike')),
)
);
return $form;
}

Related

drupal 6 add html to web forms

I am creating a custom module. I have a form that I would like to add some HTML to, but I don't know what the best way to do this is. In this example, I have a page with a textbox, dropdown list, and text area. I want to add a a div between the dropdown list and text area. But, I'm not sure how to add raw html to a web form. Here is what I have:
function myModule_add_form($form_state){
try{
$form = array();
$form['myModule_title'] = array(
'#title' => 'Title',
'#type' => 'textfield',
'#size' => '30',
'#weight'=>1,
);
$form['myModule_type_list']=array(
'#type'=>'select',
'#title' => 'Type List',
'#options' => $someArray,
'#multiple' => false,
'#attributes'=>array('size'=>1),
'#weight'=>2,
);
$form['myModule_description'] = array(
'#title' => 'Description',
'#type' => 'textarea',
'#size' => '255',
'#weight'=>3,
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => 'Submit',
'#weight'=>4,
);
return $form;
}catch(Exception $e){
$errrmsg = "Error with creating form: " .$e->getMessage();
throw New Exception($errrmsg);
}
}
Thanks
jason
Here is the reference for this:
https://api.drupal.org/api/drupal/developer!topics!forms_api_reference.html/6
I think the best option is the markup form type (This is the default type) so if you do not set the 'type' it will be markup, so you can simply insert something like this wherever you want:
$form['some_div'] = array(
'#value' => '<div>The div</div>',
);
The other option is to use prefix or suffix as also mentioned in the form API, e.g.:
$form['myModule_type_list']=array(
'#type'=>'select',
'#title' => 'Type List',
'#options' => $someArray,
'#multiple' => false,
'#attributes'=>array('size'=>1),
'#weight'=>2,
'#suffix' => '<div>The div</div>',
);
I added the div as a suffix of your list, or it could be a prefix of the text area.
Another way of doing the first option which you may prefer for readability is like this:
$form['some_div'] = array(
'#type' => 'markup',
'#prefix' => '<div>',
'#value' => 'The div content',
'#suffix' => '</div>',
);

D6: drupal_render in form causes various problems (default value, ID, date_select)

I have a problem with drupal_render (assuming that drupal_render is the right way for me to get what I want - feel free to correct me =).
I am building a form. Since the FAPI does not provide a "table"-field, I want to make one myself. My approach: use the theme()-function, specifically theme('table', ...) or theme_table(), and fill it with the respective form fields (with the intention of adding AHAH functionality later on). This forces me to use drupal_render as the value for the table cells, which causes some problems with the form elements.
The table collects numbers of employees by year, for the organisation the user is editing at this moment. The code looks as follows:
$form['employees'] = array(
'#type' => 'fieldset',
'#title' => t('Employees'),
'#collapsible' => TRUE,
'#collapsed' => FALSE,
);
$employee_query = db_query("SELECT * FROM {employees} WHERE id_organisation = %d", $org['idoOrganisation']);
$employee = array();
while ($row = db_fetch_array($employee_query)) {
$employee[] = $row;
}
$header = array(
t('Year'),
t('Total'),
t('Internal'),
t('External'),
t('Aerospace')
);
$em_delta = 0;
$rows = array();
foreach($employee as $em_delta => $value) {
$form['employees'][$em_delta]['year'] = array(
'#title' => '',
'#type' => 'date_select', // Comes with the date module
'#date_format' => $format_year,
'#date_label_position' => 'within',
'#date_year_range' => '-50:+3',
'#default_value' => $value[$em_delta]['year'],
'#id' => 'edit-employees-' . $em_delta . '-year', // Allready a quickfix, since the form is rendered without id
'#name' => 'employees['.$em_delta.'][year]', // Same here
);
$form['employees'][$em_delta]['total'] = array(
'#type' => 'textfield',
'#title' => '',
'#default_value' => $value['total'],,
'#size' => 1,
'#id' => 'edit-employees-' . $em_delta . '-total',
'#name' => 'employees['.$em_delta.'][total]'
);
$form['employees'][$em_delta]['internal'] = array(
'#type' => 'textfield',
'#title' => '',
'#default_value' => $value[$em_delta]['internal'],
'#size' => 1,
'#id' => 'edit-employees-' . $em_delta . '-internal',
'#name' => 'employees['.$em_delta.'][internal]',
);
$form['employees'][$em_delta]['external'] = array(
'#type' => 'textfield',
'#title' => '',
'#default_value' => $value[$em_delta]['external'],
'#size' => 1,
'#id' => 'edit-employees-' . $em_delta . '-external',
'#name' => 'employees['.$em_delta.'][external]',
);
$form['employees'][$em_delta]['aero'] = array(
'#type' => 'textfield',
'#title' => '',
'#default_value' => $value[$em_delta]['aero'],
'#size' => 1,
'#id' => 'edit-employees-' . $em_delta . '-aero',
'#name' => 'employees['.$em_delta.'][aero]',
);
$rows[] = array(
drupal_render($form['employees'][$em_delta]['year']),
drupal_render($form['employees'][$em_delta]['total']),
drupal_render($form['employees'][$em_delta]['internal']),
drupal_render($form['employees'][$em_delta]['external']),
drupal_render($form['employees'][$em_delta]['aero']),
);
}
$form['employees']['table'] = array (
'#value' => theme('table', $header, $rows, array(), NULL)
);
Here are the problems I am encountering:
ID- and Name-Attributes of the form elements are empty. I found something on this on the drupal site and have made my peace with it (although I don't understand it), setting those attributes manually now.
Default-values of the text fields are ignored. The fields are empty. When I let drupal_get_form render the field, the default_value shows. Someone around here suggested to set the #value-property instead, but then again I read that this is something completly different and may cause problems.
The date_select-Field is not rendered in it's entirety. The wrappers are there, the select field however appears outside of the code, just before the table (i.e. where it appears in the code).
Let's hope that's it =)
Can anybody help? What am I doing wrong?
A colleague of mine pointed out that using drupal_render within the form function is not event remotely close to being a good idea, as it removes part of the form from the whole process of validating and submitting.
Thus, figuring out why the function does not work as intended is futile. The better approach would be to simply generate the necessary amount of form fields, let them be rendered as they are within drupal_get_form(), and use the forms theme-function later on to put them into a table.
Stupid me =)

How do I add an onchange handler to a dropdown list in Drupal?

How can I add an onchange handler to a dropdown list in Drupal? The dropdown list is added using hook_form(). I have to perform a function depending on the onchange function.
Is there a way to do this?
You can add a form like this:
hook_form()
{
$form1["dropdown"] = array(
'#type' => 'select',
'#title' => t('Preview the page with themes available'),
'#options' => $ptions,
'#default_value' => 'defalut_value',
'#attributes' => array('onchange' => "form.submit('dropdown')"),
);
//Submit button:
$form1['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit '),
'#attributes' => array('style' => 'display: none;'),
);
Now you can add submit functionality using hook_submit().
Here is the simple example using '#ajax' property
$form['select'] = array(
'#type' => 'select',
'#title' => 'Option #1',
'#options' => $option,
'#ajax' => array(
// Call function that rebuilt other field
'callback' => 'ajax_load_field',
'method' => 'replace',
// div to be get replace by function output
'wrapper' => 'chart',
'effect' => 'fade'
),
);
If you generate another form field by using this drop down.
Then use AHAH for that.
$form['my_form_submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
'#weight' => 1,
'#submit' => array('my_form_submit'),//none JS version
'#ahah' => array(
'event' => 'click',
'path' => 'mymodule/js', //Your ajax function path
'wrapper' => 'myform-wrapper',
'method' => 'replace',
'effect' => 'fade',
'progress' => array(
'type' => 'bar',
'message' => t('Loading...')
)
),
While I am sure that Nicholas is way past this issue by now, this may help some who are searching for a solution.
I'm using D7 and getting the single quotes around dropdown encoded to ' I suppose by >check_plain. >How do I avoid that? – Nicholas Tolley Cottrell Jun 2 at 16:03
I just found the "Drupal" way of doing this.
Step 1, set a variable to contain dropdown by using drupal_add_js:
drupal_add_js(array('mymodule' => array('varname' => 'dropdown')), 'setting');
Step 2, add the attributes line as
'#attributes' => array('onchange' => "form.submit(Drupal.settings.mymodule.varname)"),

drupal 7. how to refactor a form array

I am creating a wizard from the Drupal example file and would like to refactor the segments of code that are repeated when setting up items like options and radios.
I have already tried a simple function passing "ordinary" and "preferential" but can't find a way to make it work.
Can someone give me an idea of the best way to do this?
unfactored code is as below:
function services_wizard_share_capital_classes($form, &$form_state) {
$form['share_classes']['type_of_class'] = array(
'#type' => 'select',
'#title' => t('What type of share will this class be?'),
'#options' => array(
1 => t('Ordinary'),
2 => t('Preferential'),
),
);
$form['ordinary']['share_type'] = array(
'#type' => 'item',
'#description' => t("You chose Ordinary Shares"),
'#states' => array(
'visible' => array(
':input[name="type_of_class"]' => array('value' => '1'),
),
),
);
$form['preferential']['share_type'] = array(
'#type' => 'item',
'#description' => t("You chose Preferential Shares"),
'#states' => array(
'visible' => array(
':input[name="type_of_class"]' => array('value' => '2'),
),
),
);
return $form;
}
I'm not sure about this being the best way but it's certainly a way to refactor your code:
function _services_wizard_share_capital_classes_add_el(&$form, $name, $description, $index) {
$form[$name]['share_type'] = array(
'#type' => 'item',
'#description' => t($description),
'#states' => array(
'visible' => array(
':input[name="type_of_class"]' => array('value' => "$index"),
),
)
);
}
function services_wizard_share_capital_classes($form, &$form_state) {
// Other code
_services_wizard_share_capital_classes_add_el($form, 'ordinary', 'You chose Ordinary Shares', 1);
_services_wizard_share_capital_classes_add_el($form, 'preferential', 'You chose Preferential Shares', 2);
// etc...
}

Why is the drupal 6 status message not showing what fields are required when I first submit the form

When I submit the form without filling in one of the required fields( or any combination of required fields) there is no status message presented to let me know I have missed the required fields.
The second time I submit the form the status message shows me what fields are required.
The status message seems to be one step behind the form submission.
If after the first submit I change what fields are filled in and I submit again then the status message that should have show the previous time will show now.
When the form is filled in correctly it submits as normal.
The form is displayed using drupal_get_form( 'otherWaysToRequest' );
This is called in a template file in the theme.
Does anyone know why the status message is one step behind?
This is a sample of the code being used
function otherWaysToRequest(&$form_state)
{
global $base_url;
$pathToTheme = path_to_theme();
$form['top-check'] = array(
'#type' => 'fieldset',
'#attributes' => array('class' => 'checkboxes'),
);
$form['top-check']['gift'] = array(
'#title' => t('Included a gift'),
'#type' => 'checkbox',
'#suffix' => '<br />',
'#required' => false,
);
$form['top-check']['contact'] = array(
'#title' => t('I would like to speak to you'),
'#type' => 'checkbox',
'#suffix' => '<br />',
'#required' => false,
);
$form['name'] = array(
'#title' => t('Name'),
'#type' => 'textfield',
'#required' => true,
);
$form['email'] = array(
'#title' => t('Email Address'),
'#type' => 'textfield',
'#required' => true,
);
$form['bottom-check'] = array(
'#type' => 'fieldset',
'#attributes' => array('class' => 'checkboxes'),
'#description' => t('<p class="Items">If you have ...:</p><p class="Items">I have included .....</p>')
);
$form['bottom-check']['share'] = array(
'#title' => t('A Share'),
'#type' => 'checkbox',
'#suffix' => '<br />',
'#required' => FALSE,
);
$form['submit'] = array(
'#type' => 'image_button',
'#src' => $pathToTheme.'/image.gif',
'#value' => t('Submit Form'),
);
}
function otherWaysToRequest_validate($form, &$form_state)
{
$mail_reg_ex = '/[-a-zA-Z0-9._]+[#]{1}[-a-zA-Z0-9.]+[.]{1}[a-zA-Z]{2,4}/';
if(!preg_match($mail_reg_ex, $form_state['values']['email']))
{
form_set_error('email', t('Invalid email address.'));
}
if( 0 == $form_state['values']['gift'] & 0 == $form_state['values']['contact'] )
{
form_set_error('gift', t('You must choose one of the first two options on the form'));
}
}
function otherWaysToRequest_submit($form, &$form_state)
{
//mail details
}
It's because by the time you're calling drupal_get_form in your template file the messages have already been committed for the current page; your validation messages will therefore show up the next time messages are displayed to the screen which is on the next page load.
You should build up the form in a custom module rather than the theme to get around this. The easiest way would be to create a block which you can assign to a region (using either hook_block in Drupal 6 or a combination of hook_block_info() and hook_block_view in Drupal 7).

Resources