【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研究輪子是腫磨做粗奶ㄉ。
結束