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

Posted by Y Cheung on Tue, Mar 21, 2017

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

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

最終效果

  • 效果圖一

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

表格表單效果圖一

  • 效果圖二

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

表格表單效果圖二

操作步驟

新建一個模組module(本文中模組命名為production,此步驟略)

production.module文件中HOOK_MENU()

 1/**
 2 * Implements hook_menu().
 3 */
 4function production_menu() {
 5  return array(
 6  'admin/production/add' = array(
 7    'title' => '添加表格表單',
 8    'access arguments' => array('access administration pages'),
 9    'type' => MENU_LOCAL_ACTION,
10    'page callback' => 'drupal_get_form',
11    'page arguments' => array('production_new_form'),
12    'file' => 'includes/production_admin.form.inc',
13  );
14);
15}

在文件production_admin.form.inc中先自定義一個標準的表單,$form_state['num']表示當前表格的總行數,然後循環打印出每一行,並且當行數不止一行的時候才現實刪除行按鈕。

 1function production_new_form($form, &$form_state) {
 2
 3  if (empty($form_state['num'])) {
 4    $form_state['num'] = 1;
 5  }
 6  $form = array(
 7    '#prefix' => '<div id="addtemplate-form-wrapper">',
 8    '#suffix' => '</div>',
 9  );
10  $form['data']['und']['#tree'] = TRUE; //很重要
11
12  for ($i = 0; $i < $form_state['num']; $i++) {
13    $form['data']['und'][$i] = array(
14      'productname' => array(
15        '#type' => 'textfield',
16        '#size' => 40,
17        '#autocomplete_path' => 'entityreference/autocomplete/single/field_productionitemname/field_collection_item/field_productionitem/NULL',
18      ),
19      'order_time' => array(
20        '#type' => 'textfield',
21        '#size' => 12,
22        '#attributes' => array(
23          'class' => ['form-text-sort', 'form-text-timefield'],
24        ),
25      ),
26      'start_time' => array(
27        '#type' => 'textfield',
28        '#size' => 12,
29        '#attributes' => array(
30          'class' => ['form-text-sort', 'form-text-timefield'],
31        ),
32      ),
33      'end_time' => array(
34        '#type' => 'textfield',
35        '#size' => 12,
36        '#attributes' => array(
37          'class' => ['form-text-sort', 'form-text-timefield'],
38        ),
39      ),
40    );
41    if ($form_state['num'] > 1) {
42      $form['data']['und'][$i]['remove_button'] = array(
43        'data' => array(
44          '#type' => 'submit',
45          '#value' => t('Remove'),
46          '#name' => 'remove-' . $i,
47          '#submit' => array('item_remove_one_submit'),
48          '#ajax' => array(
49            'callback' => 'item_add_and_remove_callback',
50            'wrapper' => 'addtemplate-form-wrapper',
51          ),
52          '#limit_validation_errors' => array(),
53        ),
54      );
55    }
56  }
57  $form['data']['actions']['add_more'] = array(
58    '#type' => 'submit',
59    '#value' => '继续添加',
60    '#limit_validation_errors' => array(),
61    '#submit' => array('item_add_more_submit'),
62    '#ajax' => array(
63      'callback' => 'item_add_and_remove_callback',
64      'wrapper' => 'addtemplate-form-wrapper',
65      'effect' => 'fade',
66    ),
67    '#weight' => 10,
68  );
69  $form['actions']['save'] = array(
70    '#type' => 'submit',
71    '#value' => '保存',
72    '#weight' => 11,
73  );
74  $form['#attached']['js'][] = libraries_get_path('jquery.timepicker') . '/include/ui-1.10.0/jquery.ui.core.min.js';
75  $form['#attached']['js'][] = libraries_get_path('jquery.timepicker') . '/include/ui-1.10.0/jquery.ui.position.min.js';
76  $form['#attached']['js'][] = libraries_get_path('jquery.timepicker') . '/jquery.ui.timepicker.js';
77  $form['#attached']['js'][] = drupal_get_path('module', 'production') . '/js/timefield.js';
78  $form['#attached']['css'][] = libraries_get_path('jquery.timepicker') . '/jquery.ui.timepicker.css';
79
80  return $form;
81}

將表單form轉成table的形式顯示。先hook_theme()theme_production_form()

 1/**
 2 * Implements hook_theme().
 3 */
 4function production_theme($existing, $type, $theme, $path) {
 5  return array(
 6    'production_new_form' => array(
 7      'render element' => 'form',
 8    ),
 9  );
10}
11
12function theme_production_new_form(&$vars) {
13  $form = $vars['form'];
14  $rows = array();
15  foreach (element_children($form['data']['und']) as $id) {
16    $rows[] = array(
17      'data' => array(
18        'productname' => drupal_render($form['data']['und'][$id]['productname']),
19        'order_time' => drupal_render($form['data']['und'][$id]['order_time']),
20        'start_time' => drupal_render($form['data']['und'][$id]['start_time']),
21        'end_time' => drupal_render($form['data']['und'][$id]['end_time']),
22        'remove_button' => drupal_render($form['data']['und'][$id]['remove_button']),
23      ),
24    );
25  }
26  $header = array(
27    'name' => '产品',
28    'order_time' => '开始生产时间',
29    'start_time' => '开始供应时间',
30    'end_time' => '结束供应时间',
31    'remove_button' => '操作',
32  );
33
34  $output = theme('table', array(
35    'header' => $header,
36    'rows' => $rows,
37    'tree' => TRUE,
38  ));
39  $output .= drupal_render_children($form);
40  return $output;
41}

添加和刪除按鈕是個ajax提交,重點就在這個submit和ajax callback function里!添加一行就是把 $form_state['num'] 增加一,反之就是減一。另一個關鍵是,要知道刪除的究竟是那一行的數據。所以 YCHEUNG 在刪除按鈕的name屬性上做文章,記錄當前的行順序ID,然後在提交中把這個這一行的填寫的數據unset刪除,並且把表格表單中填寫的數據結果集重新索引排序。最後重建表單。

 1function item_add_and_remove_callback($form, &$form_state) {
 2  return $form;
 3}
 4
 5function item_add_more_submit($form, &$form_state) {
 6  $form_state['num']++;
 7  $form_state['rebuild'] = TRUE;
 8}
 9
10function item_remove_one_submit($form, &$form_state) {
11  $button_name = explode('-', $form_state['triggering_element']['#name']);
12  if ($button_name[0] == 'remove') {
13    $row_id = $button_name[1];
14    unset($form_state['input']['und'][$row_id]);
15    $form_state['input']['und'] = array_values($form_state['input']['und']);
16  }
17  $form_state['num']--;
18  $form_state['rebuild'] = TRUE;
19}

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

Reference