在DRUPAL 7自定義多行表格表單中設置添加行按鈕與刪除行按鈕

標題有點拗口,YCHEUNG也想不出來更好的中文表達,沒懂的朋友直接看內文吧(或者看url的英文標題)。

這種功能的表格表單YCHEUNG已經嘗試了五六種方式來寫,但都不滿意。不然就是功能上多少有缺陷,比如無法使用drupal 7 form API的日期type,不然就是很繁瑣的一堆代碼和判斷,看着都頭暈……等,今天終於試出了一個令YCHEUNG較滿意的寫法!分享給大家,也是一種記錄,YCHEUNG有預感,之後還會寫很多個這樣的表單QAQ

最終效果

  • 效果圖一

頁面默認加載出來的表單只有一行,使用時如有需要則點擊繼續添加按鈕,操作一列因目前僅只有一行,不能作刪除操作,不顯示刪除按鈕。產品字段是自動補全的entity reference,三個時間字段是H:i格式用的是jQuery UI Timepicker

表格表單效果圖一

  • 效果圖二

點擊繼續添加按鈕後,表格表單增加一行,並且顯示刪除按鈕。

表格表單效果圖二

實現步驟

  • 新建一個模組module(本文中模組命名為production,此步驟略)
  • production.module文件中HOOK_MENU()
/**
 * Implements hook_menu().
 */
function production_menu() {  
  return array(
  'admin/production/add' = array(
    'title' => '添加表格表單',
    'access arguments' => array('access administration pages'),
    'type' => MENU_LOCAL_ACTION,
    'page callback' => 'drupal_get_form',
    'page arguments' => array('production_new_form'),
    'file' => 'includes/production_admin.form.inc',
  );
);
}
  • 在文件production_admin.form.inc中先自定義一個標準的表單,$form_state['num']表示當前表格的總行數,然後循環打印出每一行,並且當行數不止一行的時候才現實刪除行按鈕。
function production_new_form($form, &$form_state) {

  if (empty($form_state['num'])) {
    $form_state['num'] = 1;
  }
  $form = array(
    '#prefix' => '<div id="addtemplate-form-wrapper">',
    '#suffix' => '</div>',
  );
  $form['data']['und']['#tree'] = TRUE; //很重要

  for ($i = 0; $i < $form_state['num']; $i++) {
    $form['data']['und'][$i] = array(
      'productname' => array(
        '#type' => 'textfield',
        '#size' => 40,
        '#autocomplete_path' => 'entityreference/autocomplete/single/field_productionitemname/field_collection_item/field_productionitem/NULL',
      ),
      'order_time' => array(
        '#type' => 'textfield',
        '#size' => 12,
        '#attributes' => array(
          'class' => ['form-text-sort', 'form-text-timefield'],
        ),
      ),
      'start_time' => array(
        '#type' => 'textfield',
        '#size' => 12,
        '#attributes' => array(
          'class' => ['form-text-sort', 'form-text-timefield'],
        ),
      ),
      'end_time' => array(
        '#type' => 'textfield',
        '#size' => 12,
        '#attributes' => array(
          'class' => ['form-text-sort', 'form-text-timefield'],
        ),
      ),
    );
    if ($form_state['num'] > 1) {
      $form['data']['und'][$i]['remove_button'] = array(
        'data' => array(
          '#type' => 'submit',
          '#value' => t('Remove'),
          '#name' => 'remove-' . $i,
          '#submit' => array('item_remove_one_submit'),
          '#ajax' => array(
            'callback' => 'item_add_and_remove_callback',
            'wrapper' => 'addtemplate-form-wrapper',
          ),
          '#limit_validation_errors' => array(),
        ),
      );
    }
  }
  $form['data']['actions']['add_more'] = array(
    '#type' => 'submit',
    '#value' => '继续添加',
    '#limit_validation_errors' => array(),
    '#submit' => array('item_add_more_submit'),
    '#ajax' => array(
      'callback' => 'item_add_and_remove_callback',
      'wrapper' => 'addtemplate-form-wrapper',
      'effect' => 'fade',
    ),
    '#weight' => 10,
  );
  $form['actions']['save'] = array(
    '#type' => 'submit',
    '#value' => '保存',
    '#weight' => 11,
  );
  $form['#attached']['js'][] = libraries_get_path('jquery.timepicker') . '/include/ui-1.10.0/jquery.ui.core.min.js';
  $form['#attached']['js'][] = libraries_get_path('jquery.timepicker') . '/include/ui-1.10.0/jquery.ui.position.min.js';
  $form['#attached']['js'][] = libraries_get_path('jquery.timepicker') . '/jquery.ui.timepicker.js';
  $form['#attached']['js'][] = drupal_get_path('module', 'production') . '/js/timefield.js';
  $form['#attached']['css'][] = libraries_get_path('jquery.timepicker') . '/jquery.ui.timepicker.css';

  return $form;
}
  • 將表單form轉成table的形式顯示。先hook_theme()theme_production_form()
/**
 * Implements hook_theme().
 */
function production_theme($existing, $type, $theme, $path) {  
  return array(
    'production_new_form' => array(
      'render element' => 'form',
    ),
  );
}

function theme_production_new_form(&$vars) {  
  $form = $vars['form'];
  $rows = array();
  foreach (element_children($form['data']['und']) as $id) {
    $rows[] = array(
      'data' => array(
        'productname' => drupal_render($form['data']['und'][$id]['productname']),
        'order_time' => drupal_render($form['data']['und'][$id]['order_time']),
        'start_time' => drupal_render($form['data']['und'][$id]['start_time']),
        'end_time' => drupal_render($form['data']['und'][$id]['end_time']),
        'remove_button' => drupal_render($form['data']['und'][$id]['remove_button']),
      ),
    );
  }
  $header = array(
    'name' => '产品',
    'order_time' => '开始生产时间',
    'start_time' => '开始供应时间',
    'end_time' => '结束供应时间',
    'remove_button' => '操作',
  );

  $output = theme('table', array(
    'header' => $header,
    'rows' => $rows,
    'tree' => TRUE,
  ));
  $output .= drupal_render_children($form);
  return $output;
}
  • 添加和刪除按鈕是個ajax提交,重點就在這個submit和ajax callback function里!添加一行就是把 $form_state['num'] 增加一,反之就是減一。另一個關鍵是,要知道刪除的究竟是那一行的數據。所以 YCHEUNG 在刪除按鈕的name屬性上做文章,記錄當前的行順序ID,然後在提交中把這個這一行的填寫的數據unset刪除,並且把表格表單中填寫的數據結果集重新索引排序。最後重建表單。
function item_add_and_remove_callback($form, &$form_state) {  
  return $form;
}

function item_add_more_submit($form, &$form_state) {  
  $form_state['num']++;
  $form_state['rebuild'] = TRUE;
}

function item_remove_one_submit($form, &$form_state) {  
  $button_name = explode('-', $form_state['triggering_element']['#name']);
  if ($button_name[0] == 'remove') {
    $row_id = $button_name[1];
    unset($form_state['input']['und'][$row_id]);
    $form_state['input']['und'] = array_values($form_state['input']['und']);
  }
  $form_state['num']--;
  $form_state['rebuild'] = TRUE;
}

這樣~就寫出了一個相對乾淨的表單啦,而且可以使用各種field type,是不是很贊呢XDDD


參考: