I'm trying to use ajax to update the value of a field that can have unlimited values. I am using hook_form_FORM_ID_alter() to add a button to the form with an ajax callback. The problem is that I can not simply set $form['field_name']['language'][0]['value']
because I need to add/remove items from the fields value. So what I've done is change the value on the entity in $form_state
and build the form again. The field reloads with the items that I set but when I save the entity only the first new item saves. For example, if the field has no value then only one of the items will save; if the field has one item already then the first two items will be saved; etc.
In my specific example I am using the commerce_product form with a commerce_installments field. However, I have created a simplified example using nodes. I have created a content type called testing with an integer field called field_int_test that has a cardinality of -1 (unlimited). The "Calculate" button I have added to the form uses ajax to generate a random number of items from 2-10 for the field and sets each item's value to a random number. Clicking "Calculate" reloads the field with the new items, but saving exhibits the same behavior as above. I have checked the post and all of the values are being posted back to the web server so I am not sure where the problem is. Also, I feel that there has to be a better way to do this but I have not come up with one.
I have included the code used for the simple node example below. Any help is greatly appreciated.
/**
* Implements hook_form_FORM_ID_alter().
*/
function mymodule_form_testing_node_form_alter(&$form, &$form_state, $form_id) {
$form['calculate'] = array(
'#type' => 'button',
'#value' => t('Calculate'),
'#ajax' => array(
'callback' => 'mymodule_testing_ajax',
'event' => 'click',
'wrapper' => 'testing-replace',
'method' => 'replace',
'effect' => 'fade',
),
'#weight' => 1,
);
$form['field_int_test']['#prefix'] = '<div id="testing-replace">';
$form['field_int_test']['#suffix'] = '</div>';
}
/**
* AJAX callback that will generate a random number of items for the
* field_int_test field wiht random values for each and re-build the form with
* the new values.
*/
function mymodule_testing_ajax(&$form, &$form_state) {
$total = mt_rand(2, 10);
for ($i = 0; $i < $total; ++$i) {
$form_state['node']->field_int_test['und'][$i]['value'] = mt_rand();
}
$form_state = array(
'build_info' => array(
'args' => array($form_state['node']),
),
'node' => $form_state['node'],
'cache' => FALSE,
'no_cache' => TRUE,
'input' => array(),
);
$form = drupal_build_form('testing_node_form', $form_state);
return $form['field_int_test'];
}
I have made some changes to the callback function (see below) that now allows the field to be saved but any changes made to the node form after clicking "Calculate" will not be saved; this includes changes to the field being calculated. I have modified the callback to save the new form and form state into new variables, replace $form['field_int_test']
with the value in the new form and update the form's cache.
/**
* AJAX callback that will generate a random number of items for the
* field_int_test field wiht random values for each and re-build the form with
* the new values.
*/
function mymodule_testing_ajax(&$form, &$form_state) {
$total = mt_rand(2, 10);
for ($i = 0; $i < $total; ++$i) {
$form_state['node']->field_int_test['und'][$i]['value'] = mt_rand();
}
$field_form_state = array(
'build_info' => array(
'args' => array($form_state['node']),
),
'node' => $form_state['node'],
'cache' => FALSE,
'no_cache' => TRUE,
'input' => array(),
);
$field_form = drupal_build_form('testing_node_form', $field_form_state);
$form['field_int_test'] = $field_form['field_int_test'];
form_set_cache($form['#build_id'], $form, $form_state);
return $form['field_int_test'];
}