Vue.js cascading dropdownlist
前言
隨手記錄Vue.js連動下拉選單實現作法
我太懶只寫兩層,第三層依同樣邏輯推理就可以
如果是在資料編輯的表單畫面,網頁一載入時要先帶出用戶輸入過的值,而第二層以後的下拉選單,我到現在仍猶豫這時該交由前端發Ajax處理,或Server端先處理完再回傳前端
選擇障礙發作XD
實作
後端版本 ASP.net Core 2.1
Controller
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Newtonsoft.Json;
namespace WebApplication1VueMenu.Controllers
{
public class Option
{
public int id { get; set; }
public int pid { get; set; } //父層id,頂層為0
public string text { get; set; }
}
public class HomeController : Controller
{
private List<Option> InitOptions()
{
List<Option> options = new List<Option>();
//第一層選單
options.Add(new Option() { id = 1, pid=0, text = "A" });
options.Add(new Option() { id = 2, pid=0, text = "B" });
options.Add(new Option() { id = 3, pid=0, text = "C" });
//第二層選單
options.Add(new Option() { id = 4, pid = 1, text = "a" });
options.Add(new Option() { id = 5, pid = 1, text = "aa" });
options.Add(new Option() { id = 6, pid = 1, text = "aaa" });
options.Add(new Option() { id = 7, pid = 2, text = "b" });
options.Add(new Option() { id = 8, pid = 2, text = "bb" });
options.Add(new Option() { id = 9, pid = 2, text = "bbb" });
options.Add(new Option() { id = 10, pid = 3, text = "c" });
options.Add(new Option() { id = 11, pid = 3, text = "cc" });
options.Add(new Option() { id = 12, pid = 3, text = "ccc" });
//End 第二層選單
return options;
}
/// <summary>
/// 新增資料表單
/// </summary>
/// <returns></returns>
public IActionResult Add()
{
List<SelectListItem> options = this.InitOptions()
.Where(m=>m.pid==0)
.Select(m=>new SelectListItem() { Value = m.id.ToString() , Text = m.text }).ToList();
options.Insert(0,new SelectListItem() { Value="-1",Text="請選擇" });//Selected=true由前端v-model控制,後端不處理
ViewBag.parents = options;
return View();
}
/// <summary>
/// 編輯資料表單 (假裝用戶之前選擇了 父menu id=2 text=B, 子menu id=9 text=bbb ,這個值來自DB抓出來)
/// </summary>
/// <returns></returns>
public IActionResult Edit()
{
string userPid = "2";//這個值來自DB抓出來
ViewBag.parentid = Convert.ToInt32(userPid);
string userCid = "9";//這個值來自DB抓出來
ViewBag.childrenid = Convert.ToInt32(userCid);
//第一層選單
List<SelectListItem> options_parent = this.InitOptions()
.Where(m => m.pid == 0)
.Select(m => new SelectListItem() { Value = m.id.ToString(), Text = m.text }).ToList();
//Selected由前端v-model控制,後端不處理,就算後端指定Selected,仍然會被前端v-model蓋掉XD
options_parent.Insert(0, new SelectListItem() { Value = "-1", Text = "請選擇" });
//第二層選單
//↓這一段....以前我都在網頁第一次載入時,前端JS發出Ajax處理,現在覺得第一次網頁載入時
//應該先交由Server處理完DOM元素再回給前端,比較不會有網頁剛載入就破版(or DOM元素動來動去)問題
List<SelectListItem> options_children = this.InitOptions()
.Where(m=>m.pid==Convert.ToInt32(userPid))
.Select(m => new SelectListItem()
{ Value = m.id.ToString(), Text = m.text }).ToList();//Selected 後端不處理,由前端v-model控制
options_children.Insert(0, new SelectListItem() { Value = "-1", Text = "請選擇" });
ViewBag.parents = options_parent;
//序列化為Json陣列,讓Vue.js存取
ViewBag.childrens = JsonConvert.SerializeObject(options_children);
return View();
}
//父menu發出Ajax要資料
public IActionResult QueryChildren(int parentid)
{
List<SelectListItem> options = new List<SelectListItem>();
if ( parentid != -1 )
{
options = this.InitOptions()
.Where(m=>m.pid==parentid)
.Select(m => new SelectListItem() { Value = m.id.ToString(), Text = m.text }).ToList();
options.Insert(0, new SelectListItem() { Value = "-1", Text = "請選擇" });
}
return Content(JsonConvert.SerializeObject(options),"application/json");//統一使用JsonConvert.SerializeObject,維持物件屬性的首字大寫
}
}
}
View
Add.cshtml
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Add</title>
</head>
<body>
<form>
<div id="app">
第一層:
@Html.DropDownList("parent",(List<SelectListItem>)ViewBag.parents, new Dictionary<string, object>()
{ { "v-model.number", "parentid" }, { "v-on:change", "ParetnChange" } } )
<br />
第二層:
<select name="children" v-model.number="childrenid">
<option v-for="(c,index) in childrens" v-if="childrens.length > 0" v-bind:value="c.Value">
<!--留意 c 物件屬性有區分大小寫-->
<!--省下 v-bind:selected 單向綁定,由v-model控制-->
{{c.Text}}
</option>
</select>
<br />
<button type="button" v-on:click.prevent="MySubmit">送出</button>
<hr />
<div v-if="showResult!==''">
送出資料↓<br />
{{showResult}}
</div>
</div>
</form>
<!-- 引用jQuery -->
<script type="text/javascript" src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<!--引用Vue.js -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.22/dist/vue.js"></script>
<script type="text/javascript">
//取得第二層<option>資料的Ajax Url
let AjaxUrl = "@Url.Action("QueryChildren","Home")";
</script>
<!--javascript,通常在.js檔裡-->
<script type="text/javascript">
let app = new Vue({
el: "#app",
data: {
parentid: -1,//預設「請選擇」
childrenid:-1,//預設「請選擇」
childrens: [],
showResult:"" //顯示送出的資料(Json格式)
},
methods: {
ParetnChange: function ($event) {
let vm = this;
if (vm.parentid === -1)
{
vm.childrenid = -1;//子menu回到請選擇
vm.childrens = [];
return;
}
$.ajax({
url: AjaxUrl,
data: { parentid: vm.parentid },
method: "get",
success: function (arry)
{
vm.childrens = arry;//變更第二層選單資料
vm.childrenid = -1;//第二層寫死綁定「請選擇」
}
});
},
MySubmit: function ($event) {
let vm = this;
vm.showResult = JSON.stringify( { parentid: vm.parentid, childrenid: vm.childrenid });
}
}
});
</script>
</body>
</html>
Edit.cshtml
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Edit</title>
</head>
<body>
<form>
<div id="app">
第一層:
@Html.DropDownList("parent",(List<SelectListItem>)ViewBag.parents, new Dictionary<string, object>()
{ { "v-model.number", "parentid" }, { "v-on:change", "ParetnChange" } } )
<br />
第二層:
<select name="children" v-model.number="childrenid">
<option v-for="(c,index) in childrens" v-if="childrens.length > 0" v-bind:value="c.Value">
<!--留意 c 物件屬性有區分大小寫-->
<!--省下單向綁定v-bind:selected,由v-model控制-->
{{c.Text}}
</option>
</select>
<br />
<button type="button" v-on:click.prevent="MySubmit">送出</button>
<hr />
<div v-if="showResult!==''">
送出資料↓<br />
{{showResult}}
</div>
</div>
</form>
<!-- 引用jQuery -->
<script type="text/javascript" src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<!--引用Vue.js -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.22/dist/vue.js"></script>
<script type="text/javascript">
//取得第二層<option>資料的Ajax Url
let AjaxUrl = "@Url.Action("QueryChildren","Home")";
let parentid =@ViewBag.parentid;//user在DB裡第一層選單的值
let childrenid = @ViewBag.childrenid; //user在DB裡第二層選單的值
let childrensArry = @Html.Raw(ViewBag.childrens);
</script>
<!--javascript,通常在.js檔裡-->
<script type="text/javascript">
let app = new Vue({
el: "#app",
data: {
parentid: parentid,
childrenid: childrenid,
childrens: childrensArry,
showResult:"" //顯示送出的資料(Json格式)
},
methods: {
ParetnChange: function ($event) {
let vm = this;
if (vm.parentid === -1)
{//清掉第二層選單
vm.childrenid = -1;
vm.childrens = [];
return;
}
$.ajax({
url: AjaxUrl,
data: { parentid: vm.parentid },
method: "get",
success: function (arry)
{
vm.childrens = arry;//變更第二層選單資料
vm.childrenid = -1;//第二層寫死綁定「請選擇」
}
});
},
MySubmit: function ($event) {
let vm = this;
vm.showResult = JSON.stringify( { parentid: vm.parentid, childrenid: vm.childrenid });
}
}
});
</script>
</body>
</html>