[ASP.NET MVC][jQuery]如何讓 jQuery.ui.Autocomplete 顯示前N筆資料-兩種實作範例(Server&Client)


現在的 Web Service 越來越重視使用者經驗(User Experience),在網頁上我們也滿常看到 AutoComplete 這種 jQuery Plug-in 能讓使用者在搜尋資料上更佳的方便,就像是常見的搜尋引擎我們只需輸入幾個字就會跳出相關的資料讓我們能更快速的找到我們要的東西,前陣子在專案的上剛好碰到類似的需求,共分兩種範例以Server端來實現資料查詢已經回傳前N筆,另一部分則是透過Javascript來對我們回傳的資料進行操作,供大家參考看看 ~~

前言

現在的 Web Service 越來越重視使用者經驗(User Experience),在網頁上我們也滿常看到 AutoComplete 這種 jQuery Plug-in 能讓使用者在搜尋資料上更佳的方便,就像是常見的搜尋引擎我們只需輸入幾個字就會跳出相關的資料讓我們能更快速的找到我們要的東西,前陣子在專案的上剛好碰到類似的需求,所以就先記錄下來囉 ~

範例說明

這個範例會利用 ASP.NET MVC Web Api +jQuery-UI 來實作,並且會分為兩種範例,如下:

第一種是利用 Server 端來達到我們的需求:

透過 WebApi 服務端搭配 LINQ 操作資料庫,使用 StartWith 方法來達到從頭比較,排序後在以 Take 的函式傳回前N筆資料。

而這個作法也會比較簡單,因為搭配 LINQ 來操作資料庫可以輕鬆達到我們所要的需求。

第二種是透過 Client 端的 Javascript 來達到我們的需求(範例在範例一結束後)

結果如下圖:

開始實作

1.先建立一個簡單的 View,並在頁面上引用需要的 js 檔

@{
    ViewBag.Title = "Server";
}
@section styles{
    <link rel="stylesheet" type="text/css" href="http://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css" />
    <style>
        .ui-autocomplete {
            max-height: 100px;
            overflow-y: auto;
            overflow-x: hidden;
        }

        * html .ui-autocomplete {
            height: 100px;
        }

        td {
            padding: 0;
        }
    </style>
}

<h2>Server Base</h2>
<div>
    <input type="text" id="autocomplet_Server" />
</div>
@section scripts{
    <script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
    <script src="http://code.jquery.com/ui/1.10.3/jquery-ui.js"></script>
}

我們的 View 就真的那麼簡單 ~

2.Web Api

    public class AutoCompleteController : ApiController
    {
        private DotBlogEntities db = new DotBlogEntities();

        // GET api/AutoComplete
        public IEnumerable GetProducts(string term)
        {
            int length = 10;// length = 回傳幾筆的參數
            // term = 使用者輸入的term
           var Produtct = db.Product.Where(p => p.ProductId.StartsWith(term))
                .OrderByDescending(p => p.ProductId).Take(length)
                .Select(p => new { Id = p.ProductId, Name = p.Name }).AsEnumerable();
            return Produtct;
        }
    }

利用 WebApi 來當我們的 Web Service,參數 term 會接收使用者在前端頁面輸入的 term,並利用 StartWith 的函數做資料比對,等同於SQL 的語法 "Like term%",而返回資料後依照主鍵做排序,然後 Take 前幾筆資料,操作邏輯還滿前顯易懂的,等於我們把對資料這端的處理都交給了後端的資料庫,最後返回至頁面端,前端接收到後直接負責做資料顯示,而不用再針對資料做處理 ~

3. Javascript 部分:

這邊運用了類似 cache 的概念,記錄使用者輸入的 term 以及 response 的資料,當下次使用者輸入同樣的 term 就不會在跟 Server 請求,而是直接取出存在記憶體內的資料以減輕 Server 的負擔。

$(function () {
        var cache = {};
        $("#autocomplet_Server").autocomplete({
            minLength: 1,
            source: function (request, response) {
                var term = request.term;
                if (term in cache) {
                    response(cache[term]);
                    return;
                }

                $.getJSON("/Api/AutoComplete/GetProducts", request, function (data, status, xhr) {
                    cache[term] = data;
                    response(data);
                });
            },
            select: function (event, ui) {
                this.value = ui.item.Id;
                return false;
            }
        }).data("ui-autocomplete")._renderItem = function (ul, item) {
            return $("<li>")
        .append("<a>" + item.Id + "
" + item.Name + "</a>)
        .appendTo(ul);
        };

第二個範例是透過 Client 端的 Javascript 來達到我們的需求,如下

1.View 的部分跟範例一相同,所以就不再贅述了。

2.Web Api

        public IEnumerable GetProducts()
        {
            var Produtct = db.Product.OrderByDescending(p => p.ProductId)
            .Select(p => new { Id = p.ProductId, Name = p.Name }).AsEnumerable();
            return Produtct;
        }

這段程式碼就更簡單的,不進行任何過濾只做了排序以及 Select 我們要的欄位,最後就直接丟回頁面端。

3.Javascript 部分

先想想我們的需求是,當使用者輸入某個 term 後會從頭比較並且只顯示前N筆資料,因為 Server 端無法預先知道使用者會輸入哪些 term 所以需要在一開始載入頁面時就先將 DB 內的資料撈回來,如下:

        var data = [];
        $(window).load(function () {
            $.getJSON("/Api/AutoComplete/GetProducts", function (response, status, xhr) {
                data = response;
            });
        });

最後在利用$.grep + Regex 來篩選存放於陣列中的資料,因為 $.grep 會遍歷陣列裡的每個值,而每次會用 regex 來進行字串筆對,若有符合的則 length 值會減一,直到小於 0 之後就不再做了,程式碼如下:

       $(function () {
            var cache = {};
            $("#autocomplet_Server").autocomplete({
                minLength: 1,
                source: function (request, response) {
                    var matcher = new RegExp("^" + $.ui.autocomplete.escapeRegex(request.term), "i");
                    var length = 10;
                    var res = $.grep(data, function (item) {
                        if (length > 0) {
                            if (matcher.test(item.Id)) {
                                length = length - 1;
                                return matcher.test(item.Id);
                            }
                        }
                    });
                    response(res);
                },
                select: function (event, ui) {
                    this.value = ui.item.Id;
                    return false;
                }
            }).data("ui-autocomplete")._renderItem = function (ul, item) {
                return $("<li>") .append("<a>" + item.Id + "
 " + item.Name + "</a>") .appendTo(ul); 
          };
 });

總結

1.兩種做法都能達到相同的效果,而第一種範例也是一般實務上比較常見的作法,筆者也趁機去看了一下 Google 的搜尋機制也是透過此概念設計的,因為當使用者有輸入到這個 term 才會到資料庫去進行查詢並回傳前N筆資料,能大幅減少我們的傳輸量。

2.第二個範例比較需要注意的地方是在回傳前N筆資料的部分,筆者當初再寫的時候因為特定某個 term 符合的項目非常多,導致使用者只要輸入到那個 term 顯示出來的時間就會非常慢,最後加上了回傳前N筆資料之後就解決了。


參考連結

新手發文,如有錯誤煩請告知,感謝。
如果喜歡我的文章請按推薦,有任何問題歡迎下面留言~~~

 

 

簽名:

學習這條路很廣,喜歡什麼技術不重要,重要的是你肯花時間去學習