replace behavior not working with item form elements

robertgarrigos - December 4, 2008 - 17:20
Project:AHAH helper
Version:6.x-1.0
Component:Code
Category:support request
Priority:normal
Assigned:Wim Leers
Status:postponed (maintainer needs more info)
Description

I have a form to create an ingestion, for a diet module I'm working on. An ingestion is a group of meals (first course, second, drinks....) and I use the ahah helper module to build up a meal list subform within the ingestion form. It works great as long as I use textfields elements for the list of meals (I use the replace behavior to update the list with a new meal chosen with an autocomplete field). However, it would be great to have a list of items elements instead of textfields elements, because once a meal is chosen and added to the list I don't want the user to be able to change it (only delete). But the ahah is not working if I change the textfield element for an item element. Even if I set the meal textfield as disabled the ahah fails. Is there any structural reason, in the ahah helper module, for that?

#1

Wim Leers - December 4, 2008 - 17:46
Assigned to:Anonymous» Wim Leers
Status:active» postponed (maintainer needs more info)

The actual question is this part:

However, it would be great to have a list of items elements instead of textfields elements, because once a meal is chosen and added to the list I don't want the user to be able to change it (only delete). But the ahah is not working if I change the textfield element for an item element. Even if I set the meal textfield as disabled the ahah fails. Is there any structural reason, in the ahah helper module, for that?

So you are changing #type, right?

Basically something like:

$form['something'] = array(
  '#type' => ($some_state_or_value_is_set) ? 'item' : 'textfield',
  // …
  '#value' => ($some_state_or_value_is_set) ? $the_value : NULL,
  // …
);

Right? I don't know by heart why this wouldn't work. Could you maybe post a piece of sample code? Then I could start working and potentially debugging from that.

#2

robertgarrigos - December 7, 2008 - 17:15

this is the menu entry:

$items['diet/ingestion/%node/meal/add'] =  array(
    'title' => 'Add a meal to an ingestion',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('diet_ingestion_meal_add', 2),
    //'access callback' => true,
    'access callback' => 'node_access',
    'access arguments' => array('update', 2),
    'type' => MENU_CALLBACK,
    //'file' => drupal_get_path('modules', 'diet') . '/includes/diet.ingestion.inc',
  );

and this is the callback function to build up the form:

function diet_ingestion_meal_add($form_state, $node) {
  $form['nid'] = array('#type' => 'hidden', '#value' => $node->nid);
  $form['meal_list'] = _diet_meal_list_subform($form_state);
  $form['add_meal'] = _diet_add_meal_subform();
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Add meals'),
  );

  return $form;
}

These are the helpers functions for the previous callback function:

function _diet_meal_list_subform($form_state) {
 
  $form = array(
     '#type' => 'fieldset',
     '#title' => t('Meals'),
     '#prefix' => '<div id="meal-list-wrapper">',
     '#suffix' => '</div>',
     '#tree' => TRUE,
   );
  
  $submitted_values = (($form_state['values']['mname'] != '') && $form_state['clicked_button']['#value'] != t('Delete meal')) ? true : false;
  $i = 0;
  // This will only be printed if at least two meals have been added
  if (is_array($form_state['values']['meal_list'][0]) || !$form_state['submitted']){
    //unset meals if its delete meal button has been clicked
    foreach ($form_state['values']['meal_list'] as $key => $value) {
      if($form_state['clicked_button']['#post']['del_' .$key]) {
       unset($form_state['values']['meal_list'][$key]);
       unset($form_state['storage']["#existing_form_items"]['meal_list'][$key]);
      }
    }
    foreach ($form_state['values']['meal_list'] as $key => $value) {
        $form[$key] = array(
          '#type' => 'item',
          '#theme' => 'diet_new_meal_element',
        );
        $form[$key]['m'] = array(
          '#type' => 'textfield',
          '#title' => t('Meal'),
          '#size' => 40,
          '#value' => $form_state['values']['meal_list'][$key]['m'],
          '#theme' => 'diet_meal_element_text',
        );
       
        $form[$key]['delete_meal'] = array(
          '#type' => 'button',
          '#value' => t('Delete meal'),
          '#name' => 'del_' . $key,
        );
    }
  }
 
//print the last meal added
  if ($submitted_values && !$form_state['submitted']) {
    $i = $key + 1;
    $form[$i] = array(
      '#type' => 'item',
      '#theme' => 'diet_new_meal_element',
    );
    $form[$i]['m'] = array(
      '#type' => 'textfield',
      '#title' => t('Meal'),
      '#size' => 40,
      '#value' => $form_state['values']['mname'],
      '#theme' => 'diet_meal_element_text',
    );
    $form[$i]['delete_meal'] = array(
      '#type' => 'button',
      '#value' => t('Delete meal'),
      '#name' => 'del_' . $i,
    );
   
   
  }
  return $form;
}

function _diet_add_meal_subform() {

  $form = array(
     '#type' => 'fieldset',
     '#title' => t('Add a meal'),
     '#collapsible' => false,
   );

   $form['mname'] = array(
     '#type' => 'textfield',
     '#title' => t('Meal'),
     '#size' => 40,
     '#autocomplete_path' => 'diet/autocomplete/meal',
     '#default_value' => '',
   );
   $form['add_meal'] = array(
     '#type' => 'submit',
     '#value' => t('Add meal'),
     '#name' => 'add_meal',
     '#ahah' => array(
       'path' => ahah_helper_path(array('meal_list')),
       'wrapper' => 'meal-list-wrapper',
       'method' => 'replace',
       'effect' => 'fade',
     ),
   );
   return $form;
}

I'm sure this code could be better but this is actually working. And indeed, by only changing the type of the element $form[$key]['m'] to item the list doesn't work. What I've seen is that ,when doing that change, the element $form_state['values']['meal_list'] doesn't get posted, thus the if section

if (is_array($form_state['values']['meal_list'][0]) || !$form_state['submitted']){

doesn't get printed. Then, only the last meal is been shown in the list.

Can you see what's wrong? thanks very much for your help.

#3

robertgarrigos - December 9, 2008 - 14:27

More over: I use a theme function, based on theme_texfield, to show an element in the list in different ways:

function theme_diet_meal_element_text($element) {
  $size = empty($element['#size']) ? '' : ' size="'. $element['#size'] .'"';
  $maxlength = empty($element['#maxlength']) ? '' : ' maxlength="'. $element['#maxlength'] .'"';
  $class = array('form-text');
  $extra = '';
  $output = '';

  if ($element['#autocomplete_path'] && menu_valid_path(array('link_path' => $element['#autocomplete_path']))) {
    drupal_add_js('misc/autocomplete.js');
    $class[] = 'form-autocomplete';
    $extra =  '<input class="autocomplete" type="hidden" id="'. $element['#id'] .'-autocomplete" value="'. check_url(url($element['#autocomplete_path'], array('absolute' => TRUE))) .'" disabled="disabled" />';
  }
  _form_set_class($element, $class);

  if (isset($element['#field_prefix'])) {
    $output .= '<span class="field-prefix">'. $element['#field_prefix'] .'</span> ';
  }

//---> 3 lines to comment/uncomment to display a different form item.
  //$output .= check_plain($element['#value']);
  $output .= '<input type="text"'. $maxlength .' name="'. $element['#name'] .'" id="'. $element['#id'] .'"'. $size .' value="'. check_plain($element['#value']) .'"'. drupal_attributes($element['#attributes']) .' />';
  //$output .= '<input type="text"'. $maxlength .' name="'. $element['#name'] .'" id="'. $element['#id'] .'"'. $size .' value="'. check_plain($element['#value']) .'"'. drupal_attributes($element['#attributes']) .'" disabled="disabled" />';
 

  if (isset($element['#field_suffix'])) {
    $output .= ' <span class="field-suffix">'. $element['#field_suffix'] .'</span>';
  }

  return theme('form_element', $element, $output) . $extra;
}

There are three lines which allow you to show the item in three different ways:

....
//---> 3 lines to comment/uncomment to display a different form item.
  //$output .= check_plain($element['#value']);
  $output .= '<input type="text"'. $maxlength .' name="'. $element['#name'] .'" id="'. $element['#id'] .'"'. $size .' value="'. check_plain($element['#value']) .'"'. drupal_attributes($element['#attributes']) .' />';
  //$output .= '<input type="text"'. $maxlength .' name="'. $element['#name'] .'" id="'. $element['#id'] .'"'. $size .' value="'. check_plain($element['#value']) .'"'. drupal_attributes($element['#attributes']) .'" disabled="disabled" />';
....

Whenever you choose to show only the element value or a disabled textfield the meals list only show the last element.

#4

Wim Leers - February 14, 2009 - 16:15

Please update to version 2 and check if this problem persists.

 
 

Drupal is a registered trademark of Dries Buytaert.