I have a custom content type that contains multivalued string fields (one for primary education, one for secondary education and one for tertiary education).
In order for the user to enter multiple values for each field, I have an AJAX callback that creates additional textfield elements as per the code in the example module (with very minor modifications, to allow multiple such fields).

Adding and removing fields for fresh content works fine. However, the next step is to allow the user to edit such a node. When I specify default values for the fields (and the number entries for each field) the adding and removing fields stops working (I'm assuming it's because the elements get repopulated from database again, so the adding/removing gets overridden - See the // LOAD ALL THE EXISTING VALUES FROM DB code below).

Can anyone advise as to how I would be able to provide edit functionality to the user?

hook_menu code:

function education_menu() {
  $items = array();

  $items['education/edit/%'] = array(
    'title'            => 'Edit Education Information',
    'page callback'    => 'education_edit',
    'page arguments'   => array(2),
    'access arguments' => array('access content'),
    'type'             => MENU_CALLBACK,

The callback to load the form (and send along the node id):

function education_edit($id) {
  return drupal_get_form('education_form', $id);

The form:

function education_form($form, &$form_state, $id = '') {
  $form = array();

  if ($id != '') {
    $profile = node_load($id);
    if (isset($profile->field_education_id[$profile->language][0]['value'])) {
      $education = node_load($profile->field_education_id[$profile->language][0]['value']);

      if (isset($education->field_primary_education[$education->language])) {
        foreach ($education->field_primary_education[$education->language] as $v) {
          $form_state['values']['primary_fieldset']['primary_education'][] = $v['value'];
        $form_state['num_primary'] = count($education->field_primary_education[$education->language]);



  $form['#tree'] = TRUE;
  $form['profile_id'] = array(
    '#type'  => 'hidden',
    '#value' => $id,

  $form['primary_fieldset'] = array(
    '#type'   => 'fieldset',
    '#title'  => t('Primary Education Institutions'),
    '#prefix' => '<div id="primary-fieldset-wrapper">',
    '#suffix' => '</div>',

  // Build the fieldset with the proper number of primary education institutions. We'll use
  // $form_state['num_primary'] to determine the number of textfields to build.
  if (empty($form_state['num_primary'])) {
    $form_state['num_primary'] = 1;
  for ($i = 0; $i < $form_state['num_primary']; $i++) {
    $form['primary_fieldset']['primary_education'][$i] = array(
      '#type'          => 'textfield',
      '#title'         => t('Primary Education'),
      '#default_value' => (isset($form_state['values']['primary_fieldset']['primary_education'][$i]) ? 
        $form_state['values']['primary_fieldset']['primary_education'][$i] : ''),

  $form['primary_fieldset']['add_primary'] = array(
    '#type'   => 'submit',
    '#value'  => t('Add one more'),
    '#submit' => array('education_primary_add_more_add_one'),
    '#name'   => 'add_primary',
    '#ajax'   => array(
      'callback' => 'education_primary_add_more_callback',
      'wrapper' => 'primary-fieldset-wrapper',

  if ($form_state['num_primary'] > 1) {
    $form['primary_fieldset']['remove_primary'] = array(
      '#type'   => 'submit',
      '#value'  => t('Remove one'),
      '#submit' => array('education_primary_add_more_remove_one'),
      '#name'   => 'remove_primary',
      '#ajax'   => array(
        'callback' => 'education_primary_add_more_callback',
        'wrapper'  => 'primary-fieldset-wrapper',


  $form['submit'] = array(
    '#type'  => 'submit',
    '#value' => 'Save',

  return $form;

Callback functions for adding/removing elements:

function education_primary_add_more_callback($form, $form_state) {
  return $form['primary_fieldset'];

function education_primary_add_more_add_one($form, &$form_state) {
  $form_state['rebuild'] = TRUE;
function education_primary_add_more_remove_one($form, &$form_state) {
  if ($form_state['num_primary'] > 1) {
  $form_state['rebuild'] = TRUE;

PS: First post here ... so be gentle if I forgot to include some information :(


Only do the load thing the first time the form is built, not when it is rebuilt because of the add/remove buttons - i.e. when $form_state is empty, or more explicitely, when the $form_state['values']['primary_fieldset'] is not already set.

Also, in the add/remove submit handlers, instead of setting num_primary to +1/-1, you should be able to directly add/remove an element in $form_state['values']['primary_fieldset']['primary_education']

And instead of setting #tree on the whole form, consider setting it on just the primary_education subarray. That will simplify $form_state['values']['primary_fieldset']['primary_education'] to $form_state['values']['primary_education'] for example...

| improve this answer | |
  • Thanks, just checking whether $form_State['values']['primary_fieldset'] is set makes everything work. However, regarding editing the form in the submit handlers, I've read that form manipulation should only be performed in the function that builds the form. – MaggotMouth Feb 15 '13 at 3:31
  • You would not be editing the form in the submit handler - you would be editing $form_state. The function that [re]builds the form is already based on that $form_state. But yes, that is just a side tip for cleaner code, and not directly related to your question. So feel free to ignore it. – user13724 Feb 15 '13 at 8:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.