jqGrid 實用技巧 (三) Form Search

[anti-both]

話 說 jqGrid 的 偉 大 之 處 ,就 是 Form Editing。它 令 jqGrid 可 以 自 動 生 成 不 同 種 類 的 Form,今 天 為 大 家 介 紹 幾 個 Form Search 的 技 巧 。因 為 想 討 論 得 深 入 一 點 ,所 以 範 圍 限 定 在 最 簡 單 的 Single Search。

首 先 的 當 然 還 是 colModel,先 用 比 較 簡 單 的 資 產 名 稱 作 為 例 子 。

1
2
3
4
5
6
7
8
9
10
$("#list").jqGrid({
   url: 'data_json.php'
   ....
   colModel :[
      ....
      {name:'asset_name', index:'asset_name', search:true, stype:'text', searchrules:{required:true}, searchoptions:{sopt:['eq','cn','bw','ew']}},
      ....
   ],
   ....
});

search:true,這 很 明 顯 ,就 是 可 以 搜 尋 的 意 思 ,false 就 當 然 是 相 反 。但 有 一 點 要 注 意 ,如 果 欄 位 是 隱 藏 的 (即 hidden:true),那 該 欄 位 也 是 不 能 搜 尋 的 。

stype 用 了 text,意 思 就 是 會 出 現 一 個 text input 給 使 用 輸 入 資 料 。stype 可 以 是 text 或 者 select。

searchoptions 的 sopt,讓 我 們 可 以 選 擇 搜 尋 時 的 比 對 方 法 ,上 例 中 順 序 是 ‘equal’, ‘contains’, ‘begins with’, ‘ends with’,就 是 一 般 文 字 資 料 的 搜 尋 方 法 。另 外 還 可 以 有 ‘not equal’, ‘less’, ‘less or equal’, ‘greater’, ‘greater or equal’ 等 等 。但 重 點 是 你 可 以 針 對 每 一 個 欄 位 的 屬 性 自 定 搜 尋 的 比 對 方 法 。

searchrules 裡 面 的 required,意 思 就 是 必 須 要 輸 入 資 料 ,就 是 說 如 果 使 用 沒 有 輸 入 資 料 ,jqGrid 就 會 顯 示 一 個 錯 誤 訊 息 。因 為 使 用 者 不 應 該 去 搜 尋 名 稱 為 空 白 的 資 產 吧 ?

simple single search

在 進 一 步 討 論 更 複 雜 的 colModel 之 前 ,讓 我 們 稍 為 窺 探 一 下 jqGrid search 的 時 候 在 伺 服 器 端 的 作 法 。因 為 很 多 時 候 ,資 料 庫 都 會 很 大 ,有 很 多 筆 的 資 料 ,所 以 我 們 不 會 一 次 過 把 數 萬 筆 的 資 料 都 下 載 到 使 用 者 的 電 腦 ,再 用 它 們 建 構 一 個 超 大 型 的 grid。相 反 的 ,我 們 每 一 次 只 會 讀 取 剛 好 足 夠 的 資 料 ,通 常 一 次 讀 取 一 頁 的 資 料 就 足 夠 了 。

這 件 事 就 是 我 們 後 台 的 php 所 做 的 事 。jqGrid 的 php 範 例 寫 得 簡 單 明 瞭 ,絕 大 部 分 的 情 況 下 都 夠 用 了 。但 它 卻 沒 有 在 伺 服 器 端 做 search 的 例 子 ,令 部 分 用 家 有 一 點 困 惑 。我 在 這 裡 就 教 大 家 做 一 個 簡 單 的 single search 都 可 以 通 用 的 例 子 。下 面 的 程 式 碼 是 用 php 寫 的 ,就 是 上 面 例 子 中 的 data_json.php。

在 原 來 的 例 子 ,有 兩 句 sql。一 句 是 SELECT count(*) 的 ,用 來 取 得 所 有 資 料 的 筆 數 ,再 用 來 計 算 jqGrid 的 總 頁 數 。第 二 句 SELECT fields FROM table ORDER BY $sidx $sord LIMIT $start , $limit,這 是 用 來 取 得 本 頁 的 資 料 的 。

1
2
$SQL1 = "SELECT COUNT(*) AS count FROM assets";
$SQL2 = "SELECT * FROM assets ORDER BY $sidx $sord LIMIT $start , $limit";

我 們 要 為 它 們 加 上 一 個 條 件 式 。

1
2
$SQL1 = "SELECT COUNT(*) AS count FROM assets WHERE $condition";
$SQL2 = "SELECT * FROM assets WHERE $condition ORDER BY $sidx $sord LIMIT $start , $limit";

那 條 件 或 要 怎 麼 設 定 呢 ?那 要 在 php 檔 開 頭 的 地 方 。其 實 jqGrid 做 search 的 時 候 ,它 會 將 一 些 變 數 傳 回 到 伺 服 器 端 ,我 們 要 做 的 就 是 取 得 這 些 變 數 ,再 建 構 用 於 sql 句 子 的 where condition。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
$condition = 1;

if (isset($_GET['_search'])) {

    if ($_GET['_search']<>'false'){

    $searchField = $_GET['searchField'];
    $searchOper = $_GET['searchOper'];
    $searchString = $_GET['searchString'];

    switch ($searchOper) {
        case "eq":
            $condition .= " AND ".$searchField;
            $condition .= " =";
            $condition .= " '".$searchString."'";
            break;
        case "ne":
            $condition .= " AND ".$searchField;
            $condition .= " <>";
            $condition .= " '".$searchString."'";
            break;
        case "bw":
            $condition .= " AND ".$searchField;
            $condition .= " LIKE";
            $condition .= " '".$searchString."%'";
            break;
        case "bn":
            $condition .= " AND ".$searchField;
            $condition .= " NOT LIKE";
            $condition .= " '".$searchString."%'";
            break;
        case "ew":
            $condition .= " AND ".$searchField;
            $condition .= " LIKE";
            $condition .= " '%".$searchString."'";
            break;
        case "en":
            $condition .= " AND ".$searchField;
            $condition .= " NOT LIKE";
            $condition .= " '%".$searchString."'";
            break;
        case "cn":
            $condition .= " AND ".$searchField;
            $condition .= " LIKE";
            $condition .= " '%".$searchString."%'";
            break;
        case "nc":
            $condition .= " AND ".$searchField;
            $condition .= " NOT LIKE";
            $condition .= " '%".$searchString."%'";
            break;
        case "nu":
            $condition .= " AND ".$searchField;
            $condition .= " IS NULL";
            break;
        case "nn":
            $condition .= " AND ".$searchField;
            $condition .= " IS NOT NULL";
            break;
        case "in":
            $condition .= " AND ".$searchField;
            $condition .= " IN";
            $condition .= " ('".$searchString."')";
            break;
        case "ni":
            $condition .= " AND ".$searchField;
            $condition .= " NOT IN";
            $condition .= " ('".$searchString."')";
            break;
        case "lt":
            $condition .= " AND ".$searchField;
            $condition .= " <";
            $condition .= " '".$searchString."'";
            break;
        case "le":
            $condition .= " AND ".$searchField;
            $condition .= " <=";
            $condition .= " '".$searchString."'";
            break;
        case "gt":
            $condition .= " AND ".$searchField;
            $condition .= " >";
            $condition .= " '".$searchString."'";
            break;
        case "ge":
            $condition .= " AND ".$searchField;
            $condition .= " >=";
            $condition .= " '".$searchString."'";
            break;
    }   

    }
}

一 開 始 的 $condition = 1 的 意 思 就 是 所 有 資 料 。SELECT * FROM table WHERE 1 ,就 是 所 有 資 料 了 。而 當 前 台 傳 來 要 求 的 時 候 ,如 果 _search 變 數 不 是 false,我 們 就 開 始 加 入 要 搜 尋 的 條 件 。Single Search 的 情 況 ,每 次 只 會 搜 查 一 個 欄 位 。於 是 我 們 簡 單 地 用 switch 來 辨 別 不 同 的 searchoper ,再 從 而 構 成 我 們 的 $condition。$condition 的 樣 子 就 會 像 :1 AND asset_name=’123’。

筆 者 上 面 的 例 子 只 適 合 一 般 情 況 使 用 ,你 很 可 能 需 要 為 你 自 己 的 需 要 而 作 出 修 改 。千 萬 記 住 ,兩 個 select  句 子 都 必 需 要 加 上 相 同 的 condition ,不 然 的 話 ,你 的 分 頁 數 和 你 的 資 料 筆 數 就 會 對 不 上 了 。

好 了 ,回 到 前 台 的 jqGrid 吧 。讓 我 們 看 看 stype=’select’ 是 如 何 做 的 。在 我 的 資 料 庫 中 ,有 一 個 boolean 欄 位 叫 fix_asset,是 代 表 該 筆 資 產 是 不 是 固 定 資 產 。資 料 庫 裡 面 是 用 0 / 1 表 示 。在 jqGrid 顯 示 的 時 候 ,我 把 它 顯 示 成 是 / 否 。於 是 ,在 搜 尋 的 時 候 ,也 相 應 給 使 用 者 選 擇 是 / 否 ,而 不 要 使 用 者 輸 入 0 / 1。

1
2
3
....
{name:'fix_asset', index:'fix_asset', search:true, stype:'select', searchoptions:{value:"0:否 ;1:是 ", sopt:['eq']}, edittype:'select', editoptions:{value:"0:否 ;1:是 "}, formatter:'select'},
....

上 例 之 中 ,edittype, editoption, formatter 三 個 選 項 都 是 為 了 在 grid 裡 面 顯 示 是 /否 的 。只 有 stype 和 searchoptions 才 是 和 search form 有 關 係 的 。由 於 資 料 不 是 0 就 是 1,所 以 sopt 只 要 一 個 equal 就 夠 了 ,什 麼 大 於 小 於 contains 都 是 毫 無 用 處 的 。又 因 為 我 們 用 select 固 定 了 使 用 者 的 輸 入 數 據 ,所 以 也 不 需 要 另 外 做 data validation 了 。

Search Form Select

然 後 我 們 試 試 在 search form 裡 面 加 入 一 個 jQuery UI – datepicker。其 中 一 個 欄 位 叫 purchase_date,正 好 是 日 期 的 資 料 型 態 。這 個 部 分 稍 為 複 雜 一 點 點 ,我 們 要 使 用 到 自 定 義 函 數 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$("#list").jqGrid({
   url: 'data_json.php'
   ....
   colModel :[
      ....
      {name:'purchase_date', index:'purchase_date', search:true, stype:'text', searchoptions:{dataInit:datePick, sopt:['eq','ge','le']}, searchrules:{required:true, date:true} },
      ....
   ],
   ....
});

function datePick(elem){
   $(elem).datepicker({ dateFormat: "yy-mm-dd" });
}

先 看 看 colModel,因 為 是 使 用 text input,所 以 使 用 鍵 入 任 何 資 料 也 有 可 能 ,所 以 我 們 可 以 在 searchrules 加 上 data:true,這 樣 的 話 ,jqGrid 就 會 自 動 為 我 們 檢 查 輸 入 資 料 是 否 為 日 子 了 。因 為 資 料 型 態 是 日 期 ,所 以 sopt 就 相 應 的 只 選 擇 了 equal,greater or equal, less or equal 三 種 。要 為 search form 加 入 datapicker,我 們 可 以 在 searchoptions 裡 面 的 dataInit 加 入 一 條 自 定 義 的 函 數 ,我 把 它 叫 做 datePick。在 datePick 函 數 裡 面 ,我 就 為 input element 加 入 datepicker,並 且 把 datepicker 的 dateFormat 設 定 為 符 合 我 的 資 料 庫 使 用 的 日 期 型 態 (就 是 yyyy-mm-dd)。在 香 港 這 種 華 洋 雜 處 的 地 方 ,處 理 日 期 格 式 是 有 點 麻 煩 的 。本 來 從 小 英 式 教 學 都 是 教 dd/mm/yyyy 的 ,但 現 在 社 會 上 使 用 美 式 日 期 mm/dd/yyyy 的 也 不 少 。筆 者 習 慣 是 使 用 yyyy-mm-dd 這 樣 的 格 式 ,那 使 用 者 不 管 是 接 受 英 式 教 育 ,或 者 美 式 教 育 ,甚 至 是 外 國 人 ,也 同 樣 不 會 弄 錯 日 子 了 。

search form datepicker

除 了 datepicker,另 一 個 常 用 的 jQuery UI 就 是 autocomplete。Autocomplete 其 實 和 select 很 類 似 ,但 是 autocomplete 會 自 由 一 點 。一 般 來 說 ,如 果 選 項 是 清 晰 明 確 的 ,而 且 選 項 比 較 少 的 時 候 ,筆 者 會 使 用 select。但 是 如 果 選 項 是 比 較 雜 亂 ,又 或 者 數 量 很 多 的 時 候 ,就 會 使 用 autocomplete。比 如 說 你 有 一 千 個 選 項 ,使 用 select 來 選 其 實 也 很 痛 苦 吧 ?這 時 候 如 果 用 autocomplete 就 可 以 舒 服 很 多 。噢 ,在 這 裡 我 是 說 系 統 使 用 者 舒 不 舒 服 ,而 不 是 系 統 開 發 者 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$("#list").jqGrid({
   url: 'data_json.php'
   ....
   colModel :[
      ....
      {name:'centername', index:'centername', search:true, stype:'text', searchoptions:{dataInit:locationAutocomplete, sopt:['eq','cn']}, searchrules:{required:true} },
      ....
   ],
   ....
});

function locationAutocomplete(elem){
   $(elem).autocomplete({
      source:'locationautocomplete.php'
   });
}

跟 datepicker 類 似 ,我 們 也 使 用 了 一 條 自 定 義 函 數 locationAutocomplete,同 樣 地 方 在 dataInit 裡 面 。至 於 那 個 autocomplete 的 php 要 怎 麼 寫 ,又 或 者 autocomplete 有 些 什 麼 其 他 設 定 ,在 jQuery 的 網 站 上 就 有 非 常 詳 細 的 解 說 的 了 。

Form search autocomplete

jqGrid search 的 常 用 設 定 還 有 :closeAfterSearch, closeAfterReset, showQuery, closeOnEscape。迢 些 都 是 在 jqGrid 設 定 Navigator 的 時 候 可 以 設 定 的 。這 幾 個 設 定 的 意 思 都 很 直 觀 ,筆 者 就 不 贅 述 了 。

1
2
3
4
5
6
7
8
$("#list").jqGrid('navGrid','#pager',
   {edit:false,add:false,del:false,search:true,view:false},
   {},
   {},
   {},
   {showQuery:true, closeAfterSearch:true, closeAfterReset:true, closeOnEscape:true},
   {}
);

再 進 階 的 ,有 Multiple Search,Multiple Group,Custom Search 等 等 ,就 留 待 使 用 者 慢 慢 發 掘 吧 。

ctleung張先生,男性,肖龍。
職業:I.T. Consultant
簡介:不好好讀書;七尺差五寸,手長過膝,雙耳垂肩;性寬和,寡言語,喜怒不形於色。據說少時曾斬白蛇於鳳凰山下……

This entry was posted in jqGrid and tagged , , , , , , , , . Bookmark the permalink.

9 Responses to jqGrid 實用技巧 (三) Form Search

  1. Pippen says:

    Hi Mr. Zhang,

    还有个问题想请教一下:
    我先在的表里面有三个列,A,B,C 其中没有一列是主键。我现在想通过jqGrid去update其中的数据, 但是这样会遇到一个问题,就是写update SQL语句的时候, 我需要知道某条记录原先的值是什么, 比如:
    update table1
    SET column_A = ‘new_value’ where column_A = ‘old_value’
    在jgGrid里面通过默认的URL传递过来的是column_A的new_value, 如何的值column_A的old_value呢?
    我能够在jgGrid使用getGridParam(“selrow”)得到选中的列的值,但是怎样传递给我的URL呢? 我想到的方法是用postData,但是没有成功, 不知道错在哪里, 请指教。

    Pippen

    • Pippen says:

      我脑袋可能是锈豆了, 其中一种方法是在URL的后面附加变量和相应的值,比如data_URL = php_name.php+’&column_A=’+’&random=’+Math.random();

      有没有其他的方法呢, 不用改URL,用postData什么的。

      • Pippen says:

        Sorry, data_URL = ‘php.name.php’+’&column_A=’+column_A+’&random=’+Math.random();
        我用的是WebFOCUS,所以具体可能和大家不一样。

    • C.T. Leung says:

      我沒有實作過你說的情況,但我覺得應該沒有問題。例如我一向使用 index 作為所有資料表的主鍵的,所以我的 colModel 第一行就是 {name:’index”, index:’index’, …… }。因為我是用 json 的,所以會有 datatype: ‘json’ ,和 jsonreader:{id:’0′} 之類的東西。於是,在 editurl 時傳遞主鍵的參數一向是 id 的,和那個 index 是兩個不同的參數,所以更新主鍵也應該不會出現問題呀。

      • Pippen says:

        可能我没有表述明确我的问题。我想传递的是资料表的某列的初始的值,您所说的通过index传递过来的是提交给server的新值(比如修改某列的值至新值), 我想除了得到这些提交的新值之外,也想通过传递得到这些列在变化之前的值,那些记录在选中之后,被update之前的值。
        希望我阐述明白我的问题了, :)
        Pippen

        • C.T. Leung says:

          我想我是沒有理解錯的。例如我有一筆資料的 index 是 100,我想把它改成 102,那我還是一樣照做。

          那個 post 的參數就會有 id 和 index,id 的值就是 100(你所說的舊值),index 的值就是 102(你所說的新值)。

          sql 就是 update table set index=$index where index=$id。

          那個 id 就像是每一行的 row number。是 jqGrid 本來就有的,不用額外設定的。

          • Pippen says:

            您所举的例子中id指的是资料表中的主键吧, 就是json格式中的标示每一条记录的id,这个id不能重复, 操作后台数据库的时候就用这个id来进行删除和更新的操作。 我说的是另一种情况,就是资料表中没有主键的情况,就是说没有任何单独的一列能够标示本条记录。这种情况下, 我就需要在SQL语句的where条件中列出该条记录列的组合值,比如:
            update table
            set index=$index, col2 = ‘new_value2′
            where index=’old_value1′ col2=’old_value2’
            其中old_value是这些列在被选中的时候的值,new_value为要更改的新值。这里没有使用id,是因为我json显示的datagrid和点击edit要更新的资料表不是同一张。 这种情况可能会出现在我json显示的是一个view的数据(比如table经过了行转列的转换), 这个时候view中的id在更新table的时候是不能使用的。

            我现在使用的方法就是在URL后面追加旧值的变量,然后使用setGridParam来重新使用我定义的editurl, 不知道有么有其他的更有效的办法。
            new_url = ‘php_name.php’+’&index=’+var_index_old_value+’&col2=’+var_col2_old_value;
            jQuery(“#myjgGridname”).setGridParam({editurl:new_url});

  2. C.T. Leung says:

    Pippen!!! 首先,你自己問問題的例子就是 SET column_A=new_value WHERE column_A=old_value,這個例子完全是你自己提出的。我的解決方案也就是針對你的問題,就算 column A 不是主鍵也一樣可以的。

    你現在才提出要以 multiple-column 來決定更新那一筆資料,這根本就是一條完全不同的問題呀。不過你是尊貴的讀者,我鄭重的向你道歉,是我理解錯了你的問題。

    [ 要在更新時傳遞額外的參數,一般我們會用 editData (而不是 postData),但先生你明顯在各個方面都優秀於我,小弟就不在老兄你面前獻醜了 ]

    • Pippen says:

      张兄谦虚了,东西都有不会的点,大家互相交流,畅所欲言,共同进步,两全其美,岂不快哉? :)

Leave a Reply

Your email address will not be published. Required fields are marked *