Some surrounding thoughts and things that came up when discussing this with milkovsky. The problem looks like it came from the form not being re-built correctly.
As mentioned above any changes made to $form_state
in the ajax callback seem to get erased when everything is returned.
That's the reason for the conditional. Because you can't set values in $form_state
in the ajax call instead you put a conditional in hook_form_alter
which will only run after the ajax call has been re-made.
But by explicitly returning drupal_rebuild_form
, it appears that changes made to $form_state
in the ajax callback are saved.
So conditional can be removed and callback changed to:
function MYMODULE_ajax_rebuild_form ($form, &$form_state) {
$form_state['field']['field_collection_entity'][LANGUAGE_NONE]['items_count'] = '3';
return drupal_rebuild_form($form_state['build_info']['form_id'], $form_state, $form);
}
You can't set $form_state['rebuild'] = TRUE
in the ajax callback because it's not saved (because the form isn't rebuilt properly, which ironically is what we're trying to accomplish).
I tried putting the $form_state['rebuild'] = TRUE
into a submit handler and forcing the select field to call the submit as shown here, but that also didn't work.
If you're also trying to pre-populate the form
By default ajax reloads a cached version of the form, so changes made to default values in hook_form_alter
won't get applied when the form is rebuilt by ajax even though they'll appear in the $form
if you print it out.
In order force a non-cached form you can wipe $form_state['input']
in the ajax callback i.e.
$form_state['input'] = array();
This solution came from Reset node form after AJAX submit and you can find more detail on that question.
Hopefully the extra explanation is useful and if you can add any more please do!
A second method for loading and pre-populating field collections
I've come across a second method to do this which (at least in my situation) is more useful than the first. I'm trying to pre-populate field collections from another node and another way to do this, is to create a fake form instance and then plug it into the original form.
The method came from the final part of this blog post: http://drupal.cocomore.com/blog/field-collections-exposed
The only real differences between this example and the blog one is that this one builds the fake form in an ajax callback and rather than looping through all the fields it only changes a single one.
function ajax_get_parent_field_collection ($form, &$form_state) {
//1. load parent node
$parent_node = node_load($form_state['values']['field_node_reference'][LANGUAGE_NONE][0]['nid']);
//prebuild fields
$node_fields = field_info_instances('node', 'field_collection_entity');
//Re-create a fake form with all the trimmings which we can then attach
//as a replacement.
$tmpform = array();
$tmpform['#node'] = $parent_node;
$tmpform['type']['#value'] = 'node_type';
$tmpform_state = array('build_info' => array('form_id' => $form['#form_id']) );
field_attach_form('node', $parent_node, $tmpform, $tmpform_state, entity_language('node', $parent_node));
//replace current form with our custom version
$form['field_collection_entity'] = $tmpform['field_collection_entity'];
$form_state['field']['field_collection_entity'] = $tmpform_state['field']['field_collection_entity'];
$langcode = field_language('node', $parent_company, 'field_collection_entity');
//iterate over the fields resetting the item and revision id so new
//field collections are created rather than linking existing fc's
$field_childs = &$form_state['field']['field_collection_entity'][$langcode]['entity'];
foreach(element_children($field_childs) as $idx => $fc_entity) {
$field_childs[$idx]->item_id = NULL;
$field_childs[$idx]->revision_id = NULL;
}
//reset input to make sure cached version of the form isn't used
$form_state['input'] = array();
return drupal_rebuild_form($form_state['build_info']['form_id'], $form_state, $form);
}
#ajax
attribute on some element? Is it asubmit
button?