【JQuery】函式庫-從0到87實作Select2 之三

  • 586
  • 0

【Ajax相關】

前端透過select2.Ajax 方法,取得Controller的資料

一、透過Ajax取得資料(Controller透過Dapper取得DB資料,select2.Ajax方法,取得前者的資料)

二、關聯式下拉選單

三、Ajax傳遞多項參數

四、下拉選單 允許自建選項

*看到「參考」,點進去看就對了

本篇文章都以Ajax方式取得資料

================= 取得所有/特定書籍+關鍵字搜索 Start =================

【AjaxController 所有內容】

        //宣告連線字串已供Dapper使用

        string cnString = "Data Source=.\\ SQLExpress;Initial Catalog=SideProject;Integrated Security=True";
 

        public ActionResult Index()
        {
            var books = GetBooksList();
            var distinctCategories = books.Select(x => x.Category).Distinct();

            ViewBag.AllCategories = new SelectList(distinctCategories);


            ViewBag.AllBooks = new SelectList(books.Select(x => new
            {
                value = x.Category,
                text = x.Author + "-" + x.Name
            }), "Value", "text");

            return View();
        }

 

 

        #region 取得所有書籍
        //透過Ajax 每 點擊 / keyin 一次,就會呼叫這個方法一次
        //而透過Ajax取得Json時,若想要filter功能正常,就得在後端進行資料篩選
        //若沒有透過ajax{data{...}}的方法覆寫,則傳入的filter必須叫做q才能夠接到 參考https://select2.org/data-sources/ajax
        //必須為public
        public JsonResult GetAllBooks(string filter)
        {
            //有輸入等於篩選資料、否則為取得所有資料
            var list = string.IsNullOrWhiteSpace(filter) ? GetBooksList() : GetBooksList(filter);

            //將物件轉為IEnumerable<>, id與text為必要,丟給前端select2解析就能轉為DDL
            var modifiedData = list.Select(x => new
            {
                id = x.Id,
                text = x.Author + "-" + x.Name
            });

            return Json(modifiedData, JsonRequestBehavior.AllowGet);
        }

        private List<BOOKS> GetBooksList()
        {
            var cn = new SqlConnection(cnString);
            cn.Open();
            var list = cn.Query<BOOKS>("select * from Books").AsList();

            cn.Close();

            return list;
        }

        //前端關鍵字搜尋框有key in任何字元時

        private List<BOOKS> GetBooksList(string filter)
        {
            var cn = new SqlConnection(cnString);
            cn.Open();
            var list = cn.Query<BOOKS>($"select * from Books Where Name Like '%{filter}%'").AsList();

            cn.Close();

            return list;
        }
        #endregion
 

        #region 取得特定書籍
        public JsonResult GetSpecificBook()
        {
            var book = GetBooksList().Where(x => x.Name == "風之影");

            //將物件轉為IEnumerable<>, id與text為必要,丟給前端select2解析就能轉為DDL
            #region 參考https://select2.org/data-sources/formats 
            /*Select2 requires that each object contain an id and a text property. 
              Additional parameters passed in with data objects will be included 
              on the data objects that Select2 exposes.*/
            #endregion
            var modifiedData = book.Select(x => new
            {
                id = x.Id,
                text = x.Author + "-" + x.Name
            });
            return Json(modifiedData, JsonRequestBehavior.AllowGet);
        }
        #endregion
 

//註 DDL = DropDownList

15行       註:我的DB資料庫叫做SideProject,實際連線字串因人而異,需稍做修改。

39行       前端Index View生成的下拉式選單,每次點擊/輸入字元時都會觸發Ajax呼叫此方法,是否輸入字元會影響資料,詳見42行

               注意,select2本身的接受值是『q』,因此此處傳入的filter需搭配前端第30-35行(下面補圖會紅框標註)覆寫

               也就是如果沒有覆寫,39行傳入值就必須是 (string q) 才接得到前方KeyIn的字元 詳見https://select2.org/data-sources/ajax

42行       三元運算子,若string.IsNullOrWhiteSpace(filter)成立,則list = GetBooksList();不成立,則list = GetBookList(filter)

69行       透過Dapper的模糊搜尋,若有輸入字元,就會以該字元做為搜尋條件。此為其中一種資料篩選的方式(因為我沒研究出其他種)

AjaxController-【Index】


@{
    Layout = "~/Views/Shared/_Layout.cshtml";
}
<string>取得所有書籍</string>
<select class="Books" style="width:580px"></select>
<br /><br />

<string>取得單一書籍</string>
<select id="select2Transforming" style="width:580px"></select>
<br /><br />

@section Scripts
{
    <script type="text/javascript">
        $(document).ready(function () {

            //========取得所有書籍 Start========
            $(".Books").select2({
                placeholder: "取得所有書籍",
                allowClear:true,
                ajax: ({
                    url: "/Ajax/GetAllBooks",
                    dataType: "json",
                    processResults: function (data) {
                        return {
                            results: data //必須為results 參考https://select2.org/data-sources/formats
                        };
                    },
                    data: function (params) {
                        return {
                            //若沒有透過ajax{data{...}}的方法覆寫,則傳入Controller的filter必須叫做q才能夠接到 
                            //參考https://select2.org/data-sources/ajax //對應Controller第37行
                            filter: params.term //search term
                        }
                    }
                }),
            });
            //========取得所有書籍 End========

            //========取得單一書籍 Start========
            $("#select2Transforming").select2({
                placeholder: "取得單一書籍",

                ajax: {
                    url: "/Ajax/GetSpecificBook",
                    dataType: "json",
                    processResults: function (data) {
                        return {
                            results: data
                        };
                    }
                }
            });
            //========取得單一書籍 End========
        });
    </script>
}

23行 ajax方法內的url表示你要呼叫的網址/位置 以這行來說 會呼叫Ajax(Controller)的GetAllBooks方法,至於傳入值(有輸入關鍵字)的處理,詳見30-35行

25行 processResults是select2中的寫法,我的理解會比較像是 try catch finally裡面finally的功用(但我的理解不一定是對的)

        這邊也可以使用一般ajax的success與error去做一些自己想要的判斷 ,值得注意的是,這邊return的東西必須叫做results

        我的理解是,function(data)裡面的data就是你各位透過Ajax從Controller取得的資料,再把結果賦予results

30行 function(params)的params 代表 下拉式選單內關鍵字搜尋框,你所key in的字元。

實際效果如下圖

建議透過中斷點觀察每次點擊/key in資料時,傳給Controller的字元。

================= 取得所有/特定書籍+關鍵字搜索 End =================

================= 取得書籍類別 + 關鍵字搜索 + 關聯下拉選單 Start =================

【AjaxController】 加上

 #region 取得書籍類別 + 關鍵字搜索 + 關聯下拉選單
        public JsonResult GetBooksByCategory(string Category)
        {
            var list = GetBooksList().Where(x => x.Category.ToString() == Category);

            var modifiedData = list.Select(x => new
            {
                id = x.Id,
                text = x.Author + "-" + x.Name
            });

            return Json(modifiedData, JsonRequestBehavior.AllowGet);
        }
        #endregion

        #region 取得書籍類別 + 關鍵字搜索(多選) + 關聯下拉選單
        public JsonResult GetBooksByCategories(string Categories)
        {
            //Categories為一個以上時,會是 "英文,文學"的資料型態

            List<BOOKS> allBooks = GetBooksList();
            List<string> CategoryList = new List<string>();

            CategoryList = Categories.Split(",".ToCharArray(), StringSplitOptions.RemoveEmptyEntries).ToList();

            //用來裝載符合傳入值的資料,用以回傳至前端
            List<BOOKS> books = new List<BOOKS>();

            foreach (var eachCategory in CategoryList)
            {
                var BooksByCategory = allBooks.Where(x => x.Category.ToString() == eachCategory).ToList();
                foreach (var eachBookByCategory in BooksByCategory)
                {
                    books.Add(eachBookByCategory);
                }
            }

            var modifiedData = books.Select(x => new
            {
                id = x.Id,
                text = x.Author + "-" + x.Name
            });

            return Json(modifiedData, JsonRequestBehavior.AllowGet);
        }
        #endregion

112行 多選時傳入的Categories,若為一個以上,傳入的資料會是 "文學,科幻"的樣子

117行 宣告用以裝載解析Categories後的字串陣列

119行 解析Categories

124行 foreach解析後(傳入的)Category

126行 找出對應Category的所有書籍

127行 foreach對應Category的所有書籍

129行 加入List<Books>

 

 

AjaxController -【Index】


@{
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<string>取得書籍類別+關鍵字搜索+關聯下拉選單</string>
@Html.DropDownList("NormalSelectAndCascadDDL", (IEnumerable<SelectListItem>)ViewBag.AllCategories, null, new { @class = "form-control", @id = "NormalSelectAndCascadDDL" })
@Html.DropDownList("AllBooks", (IEnumerable<SelectListItem>)ViewBag.AllBooks, null, new { @class = "form-control", @id = "AllBooks" })
<br /><br />

<string>取得書籍類別+關鍵字搜索(多選)+關聯下拉選單</string>
@Html.DropDownList("MultiSelectAndCascadDDL", (IEnumerable<SelectListItem>)ViewBag.AllCategories, null, new { @class = "form-control", @id = "MultiSelectAndCascadDDL", @multiple = "multiple" })
<select id="NullDDLForMulti" class="form-control"></select>
<br /><br />

@section Scripts
{
    <script type="text/javascript">
        $(document).ready(function () {
            //========取得書籍類別 + 關鍵字搜索 + 關聯下拉選單 Start========
            $("#NormalSelectAndCascadDDL").select2();

            $("#NormalSelectAndCascadDDL").change(function () {
                $("#AllBooks").empty();
                $.ajax({
                    type: 'Post',
                    url: '@Url.Action("GetBooksByCategory")',
                    dataType: 'json',
                    data: { Category: $("#NormalSelectAndCascadDDL").val()},

                    success: function (AllBooks) {
                        $.each(AllBooks, function (i, AllBooks) {
                            $("#AllBooks").append('<option value="' + AllBooks.id + '">' +
                                AllBooks.text + '</option>');
                        });
                    },
                    error: function (ex) {
                        alert('GetBooksByCategory Failed' + ex);
                    }
                })
            })
            //========取得書籍類別 + 關鍵字搜索 + 關聯下拉選單 End========

            //========取得書籍類別 + 關鍵字搜索(多選) + 關聯下拉選單 Start========
            $("#MultiSelectAndCascadDDL").select2();

            $("#MultiSelectAndCascadDDL").change(function () {
                var CategoryList = $(this).val();
                $.ajax({
                    type: "Post",
                    url: "/Ajax/GetBooksByCategories?Categories=" + CategoryList,
                    dataType: "json",
                    success: function (dataFromGetBooksByCategories) {
                        $("#NullDDLForMulti").empty(); //需要存在,否則會重複
                        $.each(dataFromGetBooksByCategories, function (index, row) {
                            $("#NullDDLForMulti").append("<option value='" + row.Id + "'>" +
                                row.text + "</roption>")
                        });
                    },
                    error: function (ex) {
                        alert('GetBooksByCategories Failed' + ex);
                    }
                });
            });
            //========取得書籍類別 + 關鍵字搜索(多選) + 關聯下拉選單 End========

        });
    </script>
}

實際效果如下圖上為載入時的畫面

第8行   搭配此處的所有書籍(ID為AllBooks的下拉選單) ,是透過ViewBag的方式預先載入的​

第21行 初始化select2

第23行 當NormalSelectAndCascadDDL(取得書籍類別 + 關鍵字搜索 + 關聯下拉選單)被改變時

第24行 清空ID為AllBooks的下拉選單

第25行 觸發ajax

第29行 data意味著,要傳什麼資料到controller,這邊名稱我取為Category,看到Controller第97行,也必須用相同名字才能接得到

第31行 Ajax成功取回的資料就會放在AllBooks,這邊使用.each,將取回的Allbooks內的id與text分別"畫"到Html上

            =>正式選擇其中一個選項時

 

多選

48行 抓取id為MultiSelectAndCascadDDL的資料做為傳到Controller的值

51行 將抓取的資料傳到Controller做為搜尋條件

除了傳入值的部分,其餘和單選時差不多,唯一值得注意的是

多選時,每一次的呼叫,需要先將$("#NullDDLForMulti")清空,可以研究少了這行,會有什麼變化、以及為何。

另一個重點往上翻到Controller對於多選時的資料解析、篩選。

實際效果如下圖

================= 取得書籍類別 + 關鍵字搜索 + 關聯下拉選單 End =================

================= 傳遞多重Parameters Start =================

【AjaxController】加上

#region 測試傳遞多重parameter
        public JsonResult GetLiteralJson(string searchCategory, string testString, string[] testIntArrayToString)
        {
            var list = GetBooksList().Where(x => x.Category.ToString() == searchCategory);

            //testString,testIntArrayToString僅為測試select2覆寫ajax.data傳遞多參數
            #region 解析string[]
            //1.前端JSON.stringify會被int[]轉成string[]傳到此處
            //2.透過JavaScriptSerializer().Deserialize<IList<int>>(testIntArrayToString[0]);
            //  轉型成IList<int>,之後可視需求進行取資料
            IList<int> Ids = new JavaScriptSerializer().Deserialize<IList<int>>(testIntArrayToString[0]);
            #endregion

            var modifiedData = list.Select(x => new
            {
                id = x.Id,
                text = x.Author + "-" + x.Name
            });

            return Json(modifiedData, JsonRequestBehavior.AllowGet);
        }
        #endregion

AjaxController - 【Index】


@{
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<string>取得文學書籍 + 測試select2傳遞多參數</string>
<select class="LiteralBooks" style="width:580px"></select>
<br /><br />

@section Scripts
{
    <script type="text/javascript">
        $(document).ready(function () {
            //========測試傳遞多重parameter Start========
            $(".LiteralBooks").select2({
                placeholder: "LiteralBooks",
                theme: "classic",

                //有>一個參數時
                /*select2 覆寫ajax.data 參考https://select2.org/data-sources/ajax
                /Sometimes, you may need to add additional query parameters to the request.
                 * You can modify the parameters that are sent with the request
                 * by overriding the ajax.data option:*/
                ajax: {
                    url: "/Ajax/GetLiteralJson",
                    dataType: "json",
                    data: function (params) {
                        var query = {
                            searchCategory: '文學',
                            testString: '風之影,時間迴旋,原子習慣',
                            testIntArrayToString: JSON.stringify([1, 2, 3, 4, 5]), //JSON.stringify會被int[]轉成string[]傳給Controller
                        }
                        return query;
                    },
                    processResults: function (data, params) {
                        return {
                            results: data
                        };
                    }

                }
            });
            //========測試傳遞多重parameter End========

        });
    </script>
}

這裡純粹關注 28,29,30行所傳入的(複數)參數,再看到Controller的中斷點

以及 154行 解析testIntArrayToString後的資料格式

================= 傳遞多重Parameters End =================

================= 允許自建選項(Free Key In) Start =================

AjaxController沒有需要添加的東西

AjaxController - 【Index】

@{
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<string>允許自建選項(Free Key In)</string>
<select id="tagsTrue" style="width:580px"></select>
<br /><br />

<string>允許自建選項(createTag)</string>
<select id="tagsTrueWithcreateTag" style="width:580px"></select>
<br /><br />

<string>允許自建選項(insertTag)</string>
<select id="tagsTrueWithinsertTag" style="width:580px"></select>
<br /><br />

<string>允許自建選項(多選)(Free Key In)</string>
<select id="tagsTrueMulti" style="width:580px" multiple="multiple"></select>
<br /><br />

<string>允許自建選項+多選+分隔符號「,」、「 」</string>
<select id="tagsTrueMultiWithSeparators" style="width:580px"></select>
<br /><br />

@section Scripts
{
    <script type="text/javascript">
        $(document).ready(function () {
            //========允許自建選項(Free Key In) Start========
            $("#tagsTrue").select2({
                placeholder: "允許自建選項(Free Key In)",
                tags: true,
                ajax: {
                    url: "/Ajax/GetAllBooks",
                    dataType: "json",
                    processResults: function (data) {
                        return {
                            results: data
                        };
                    }
                }
            });

            $("#tagsTrueWithcreateTag").select2({
                placeholder: "允許自建選項(允許自建選項(createTag))",
                tags: true,
                ajax: {
                    url: "/Ajax/GetAllBooks",
                    dataType: "json",
                    processResults: function (data) {
                        return {
                            results: data
                        };
                    }
                },
                createTag: function (params) {
                    var term = $.trim(params.term);

                    if (term === '') { //輸入空白,則不建立選項
                        return null;
                    }

                    //Don't offset to create a tag if there is no 「#」 symbol
                    if (term.indexOf('#') === -1) {
                        return null;
                    }

                    return {
                        id: term,
                        text: term,
                        newTag: true // add additional parameters
                    }
                }
            });

            $("#tagsTrueWithinsertTag").select2({
                placeholder: "允許自建選項(允許自建選項(insertTag))",
                tags: true,
                ajax: {
                    url: "/Ajax/GetAllBooks",
                    dataType: "json",
                    processResults: function (data) {
                        return {
                            results: data
                        };
                    }
                },
                createTag: function (params) {
                    var term = $.trim(params.term);

                    if (term === '') { //輸入空白,則不建立選項
                        return null;
                    }

                    //Don't offset to create a tag if there is no 「#」 symbol
                    if (term.indexOf('#') === -1) {
                        return null;
                    }

                    return {
                        id: term,
                        text: term,
                        newTag: true // add additional parameters
                    }
                },
                insertTag: function (data, tag) {
                    data.push(tag);
                }
            });

            $("#tagsTrueMulti").select2({
                placeholder: "允許自建選項(多選)(Free Key In)",
                tags: true,
                ajax: {
                    url: "/Ajax/GetAllBooks",
                    dataType: "json",
                    processResults: function (data) {
                        return {
                            results: data
                        };
                    }
                }
            });

            $("#tagsTrueMultiWithSeparators").select2({
                placeholder: "允許自建選項+多選+分隔符號「,」、「 」",
                tags: true,
                multiple: true,
                tokenSeparators: [',', ' '],
                ajax: {
                    url: "/Ajax/GetAllBooks",
                    dataType: "json",
                    processResults: function (data) {
                        return {
                            results: data
                        };
                    }
                }
            });
            //========允許自建選項(Free Key In) End========

        });
    </script>
}

實際效果如圖

這邊值得注意的只有63&95行 如同敘述,若沒有"#"符合則不給新增選項

結合Ajax傳遞多參數,應該是有蠻多應用可以嘗試的,就看實際上遇到什麼需求惹。

至於允許自建選項和分隔符號,我遇到了一些BUG但始終還是找不太到原因,key一個字有可能變成兩個

================= 允許自建選項(Free Key In) End =================

 

結論

select2真的是很好用的套件,值得花時間去研究,前人造好輪子,我們只需要學習輪子怎麼使用,而不用自己閉門造車~~

當然如果很威猛的人也可以試著去找source Code研究輪子是腫磨做粗奶ㄉ。

結束