I am attempting to use ajax api on a form

my code sample has 2 ajax calls in, the problem is with the $form['one'] call (the other one is fine)
The problem is that it runs once and only once (without a full page reload)
I have checked the code with xdebug suring the first ajax call and everything seams to be in order.

The form is initially generated with hook_menu using drupal_get_form()

What is going wrong here?

------------------------------------

// My form array
function ajax_play_simplest($form, &$form_state) {
$form = array();
$form['changethis'] = array(
'#type' => 'select',
'#options' => array(
'one' => 'one',
'two' => 'two',
'three' => 'three',
),
'#ajax' => array(
'callback' => 'ajax_play_example_simplest_callback',
'wrapper' => 'replace_textfield_div',
),
);

// This entire form element will be replaced with an updated value.
$form['replace_textfield'] = array(
'#type' => 'textfield',
'#title' => t("The default value will be changed"),
'#value' => t('the default valoo'),
'#description' => t("This will change"),
'#prefix' => '

',
'#suffix' => '

'
);

$form['one'] = array (
'#type' => 'textarea',
'#cols' => 2,
'#title' => t('textarea one'),
'#description' => t('this will show the content of the text box'),
'#ajax' => array(
'callback' => 'callback_one',
'wrapper' => 'textarea_one',
),
'#prefix' => '

',
'#suffix' => '

'
);

return $form;
}

//the ajax callback

function callback_one($form, $form_state){
$form['one'] = array(
'#type' => 'textarea',
'#cols' => 5,
'#title' => t('textarea one'),
'#description' => (!empty($form_state['values']['one'])
? $form_state['values']['one'] : t("Not changed yet")) . "'",
'#ajax' => array(
'callback' => 'callback_one',
'wrapper' => 'textarea_one',
),
'#prefix' => '

',
'#suffix' => '

'
);

return $form['one'];
}

Comments

nevets’s picture

All changes to the form must be made in the form function. The ajax function should not change anything, its job is to return the form element(s) that are being changed or inserted.

iconofsin’s picture

In the case what do i do if i want to modify a form in response to user actions
for example I may have a countries field and a counties/states/regions field and i may want the list of counties/states/regions to change when the user selects a different country.
This is quite common functionality and a very typical example of AJAX use.

Would I need to re-call the hook_form function from the ajax function and code the hook_form function in such a way that it builds the form differently depending on if it is being called on the initial page load or if ajax has called it? if yes.....

Would i need to re-build the whole form each time or just the object which should change?
and
can hook form accept any extra parameters - which i would use to pass in a variable which determines where it is being called from and what should change?

Jaypan’s picture

The form is already automatically rebuilt every time. This is what happens on a normal non-ajax form submission with Drupal:

1) The form is built
2) All _form_alter() functions alter the form
3) The php array representation of the form is cached in the database
4) The form is sent to the browser
5) The user submits the form
6) The submitted values are compared with the cached form, to check if the submitted values could have been submitted from that form. If they are, the form submission is allowed.

This is what happens when you include AJAX into the mix. The start and end are the same, but there is more in the middle:

1) The form is built
2) All _form_alter() functions alter the form
3) The php array representation of the form is cached in the database
4) The form is sent to the browser

5) User makes change that causes some ajax.
6) The values are sent to the server in a background call using JavaScript
7) The submitted values are checked against the database to see if the submitted values are valid
8) If the are, the form is rebuilt, passing $form_state['values'] to the form rebuild
9) All _form_alter() functions are called
10) The new form is cached in the database, replacing the old one
11) The form is sent to the AJAX callback function
12) The AJAX callback function determines which parts of the form are to be sent back to the browser
13) The new parts are sent back to the browser
14) The browser inserts them into the form

15) The user submits the form
16) The submitted values are compared with the cached form, to check if the submitted values could have been submitted from that form. If they are, the form submission is allowed.

As you can see, in step 10, the form is cached, and the ajax callback function is called after that. This is why changes need to be made in the form definition, and not in the ajax callback.

iconofsin’s picture

So- taking my example of a region/state/county field which gets a new list of values when the country field changes - would I be correct to believe that I put something like the following in the hook_form function

if (!isset($form_state['countries']))
{
regions['value'] = some default
}
else
{
some logic which fetches a list of regions for the selected country
regions['value'] = list of regions in that country
}

The TRUE IF condition runs when the page is made
The ELSE IF condition would run if an AJAX call has been made, and the region value has been changed.

if this is correct
What,if any, code would i need to place in the ajax function?

Jaypan’s picture

You've got the right idea, but you want $form_state['values']['countries'].

You need to return the relevant code from your AJAX callback.

iconofsin’s picture

yay, i got it working

there is still a bit of confusion

I have the following setup (only relevant code is shown)

hook_menu()
{
'page callback' => 'main_page',
}

function main_page() {
    return drupal_get_form('ajax_play_simplest');
}

ajax_play_simplest($form, &$form_state)
{
$form['one'] = array (
        '#type' => 'textarea',
        '#cols' => 2,
        '#title' => t('textarea one'),
        '#description' => (!empty($form_state['values']['one'])
                ? $form_state['values']['one'] : t("Not changed yet")) . "'",
        '#ajax' => array(
            'callback' => 'callback_one',
            'wrapper' => 'textarea_one',
        ),
        '#prefix' => '<div id="textarea_one">',
        '#suffix' => '</div>'
    );
}

function callback_one($form, $form_state){

    return $form;
}

When the page first loads hook_menu calls main_page() which uses drupal_get_form() to create the form properly
drupal_get_form() is not called from the ajax callback so how is the form being rendered properly?

In addition here is the full code (in case i was mistaken about what is relevant here)


function main_page() {
    return drupal_get_form('ajax_play_simplest');
}

function ajax_play_simplest($form, &$form_state) {
    $form = array();

    $form['changethis'] = array(
        '#type' => 'select',
        '#options' => array(
            'one' => 'one',
            'two' => 'two',
            'three' => 'three',
        ),
        '#ajax' => array(
            'callback' => 'ajax_play_example_simplest_callback',
            'wrapper' => 'replace_textfield_div',
        ),
    );

    // This entire form element will be replaced with an updated value.
    $form['replace_textfield'] = array(
        '#type' => 'textfield',
        '#title' => t("The default value will be changed"),
        '#value' => t('the default valoo'),
        '#description' => t("This will change"),
        '#prefix' => '<div id="replace_textfield_div">',
        '#suffix' => '</div>'
    );

    $form['one'] = array (
        '#type' => 'textarea',
        '#cols' => 2,
        '#title' => t('textarea one'),
        '#description' => (!empty($form_state['values']['one'])
                ? $form_state['values']['one'] : t("Not changed yet")) . "'",
        '#ajax' => array(
            'callback' => 'callback_one',
            'wrapper' => 'textarea_one',
        ),
        '#prefix' => '<div id="textarea_one">',
        '#suffix' => '</div>'
    );

    $form['a_button'] = array (
        '#type' => 'button',
        '#value' => 'one should click me',
        '#ajax' => array(
            'callback' => 'callback_button',
            'wrapper' => 'block-cmis-cmis-switcher'
        ),
    );

    return $form;
}

// Ajax callback functions
function ajax_play_example_simplest_callback($form, $form_state) {
    // The form has already been submitted and updated. We can return the replaced
    // item as it is.

    $form['replace_textfield'] = array(
        '#type' => 'textfield',
        '#title' => t("The default value will be changed"),
        '#value' => t('the default valoo'),
        '#description' => t("Say something about why you chose") . "'" .
            (!empty($form_state['values']['changethis'])
                ? $form_state['values']['changethis'] : t("Not changed yet")) . "'",
        '#prefix' => '<div id="replace_textfield_div">',
        '#suffix' => '</div>'
    );

    return $form['replace_textfield'];
}

function callback_one($form, $form_state){

    return $form['one'];
}

function callback_button(){
    return 'bye bye cmis';
}

// create las page with la form upon it
function ajax_play_menu()
{
    $items['ajax_play'] = array(
        'title' => 'ajax_play',
        'page callback' => 'main_page',
        'access arguments' => array('administer news feeds')
    );
    return $items;
}
Jaypan’s picture

Can you please edit your post and wrap your code in <?php ?> tags to make it more readable? I'll look at your code after that.

Thank you.

iconofsin’s picture

done.

Jaypan’s picture

When the page first loads hook_menu calls main_page() which uses drupal_get_form() to create the form properly
drupal_get_form() is not called from the ajax callback so how is the form being rendered properly?

The ajax callback processes the form (which happens on the initial page generation inside drupal_get_form()), then passes the generated form to your ajax callback. This is why you don't call drupal_get_form() inside the ajax callback - by the time the ajax callback function is called, all of the form processing has been completed. Think of it as if the system called drupal_get_form() for you already.

iconofsin’s picture

would a a stack trace placed inside the ajax callback reveal that the drupal_get_form() function has in fact been called prior to the ajax callback?

Jaypan’s picture

No, because it's not called in ajax processes. I believe drupal_process_form() is called if I remember correctly. This is also called within drupal_get_form(). Although whether this will show up in the stack in the ajax callback is questionable - the entire process may be completed before the ajax callback is called, and if so, wouldn't show up in the stack.