標題有點拗口,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