[ASP.net MVC4] 使用@Html.DropDownListFor()+Partial View部份檢視實現連動下拉選單CascadingDropDown(無for-loop方式)
前言
之前文章提過,要產生表單輸入標籤:[ASP.net MVC 4] 生成控制項的方式選擇(以文字方塊TextBox為例)
儘量使用@Html.XXXFor()方式,可以享有intellisense不容易打錯字、ViewModel修改屬性名稱時Visual Studio可以在編譯時期幫忙檢查錯誤的好處
但
連動下拉選單的第二層DropDownList呢?
一般做法在Controller組完第二層下拉選單的Html字串或Json字串後,回傳給Ajax的callback function來顯示第二層下拉選單
※參見:[ASP.net MVC] ViewModel修改資料+DropDownList下拉選單連動
如果想改用@Html.DropDownListFor()的話?
請見以下
實作
1.連動下拉選單的傳統做法流程
Step 1.當第一層下拉選單觸發javascript的onchange事件
Step 2.在onchange事件中,取得第一層下拉選單選擇的值
Step 3.把選擇的值透過Ajax傳給Controller的Action
Step 4.Action依據剛剛的值產生不同的第二層選單List並回傳
Step 5.Ajax的callback function接到回傳值後,顯示在View上
改用@Html.DropDownListFor()方式,流程也是一樣
但由於要用Html Helper來呈現第二層下拉選單,所以在第4步驟需要追加一個Partial View(部份檢視),放入@Html.DropDownListFor()語法
2.
因為部份檢視裡會用到@Html.DropDownListFor()來顯示第二層下拉選單,此部份檢視(Partial View)可以宣告使用ViewModel
在Models資料夾加入SecondLevelViewModel.cs類別
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace MvcApplicationDropDownList.Models
{
public class SecondLevelViewModel
{
public string FirstLevel { get; set; }
public string SecondLevel { get; set; }
}
}
Controller完整代碼
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MvcApplicationDropDownList.Models;
namespace MvcApplicationDropDownList.Controllers
{
public class HomeController : Controller
{
//資料來源
//第一層資料
List<string> firstLevelItems = new List<string>() {
"A","B"
};
//第二層資料
List<SecondLevelViewModel> secondLevelItems = new List<SecondLevelViewModel>();
public HomeController()
{
//第一層有A、B
//第二層為{A1、A2、A3}或{B1、B2}
secondLevelItems.Add(new SecondLevelViewModel() { FirstLevel = "A", SecondLevel = "A1"});
secondLevelItems.Add(new SecondLevelViewModel() { FirstLevel = "A", SecondLevel = "A2" });
secondLevelItems.Add(new SecondLevelViewModel() { FirstLevel = "A", SecondLevel = "A3" });
secondLevelItems.Add(new SecondLevelViewModel() { FirstLevel = "B", SecondLevel = "B1" });
secondLevelItems.Add(new SecondLevelViewModel() { FirstLevel = "B", SecondLevel = "B2" });
}
[HttpGet]
public ActionResult Index()
{
//用Linq撈出第一層資料,資料來源可能是DB、Web API...等等
var items = (from c in firstLevelItems
select new SelectListItem { Text=c,Value=c }).ToList();
//加入「請選擇」
items.Insert(0, new SelectListItem() { Text="請選擇",Value="-1" });
ViewData["FirstLevelItems"] = items;
return View();
}
//使用Get Method是為了讓瀏覽器輸入Url方便觀察值的變化
[HttpGet]
public ActionResult ShowSecondDropDownList(string FirstLevel)
{
//使用Linq撈出第二層的資料
var items = (from s in secondLevelItems
where s.FirstLevel == FirstLevel
select new SelectListItem { Text=s.SecondLevel,Value=s.SecondLevel }).ToList();
//加入「請選擇」
items.Insert(0, new SelectListItem() { Text = "請選擇", Value = "-1" });
ViewData["SecondLevelItems"] = items;
return View();
}
}
}
在Views資料夾下加入Shared資料夾並加入部份檢視ShowSecondDropDownList.cshtml
@model MvcApplicationDropDownList.Models.SecondLevelViewModel
@Html.DropDownListFor(Model=>Model.SecondLevel,(IEnumerable<SelectListItem>)ViewData["SecondLevelItems"])
而Index.cshtml
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script type="text/javascript">
$(document).ready(init);
function init()
{
//當網頁上的DOM都載入時,第一層下拉選單也呈現出來了
//↓顯示第二層下拉選單,沒加這兩段↓的話,第一次網頁載入時第二層下拉選單不會出現
var FirstLevel = $("select[name='YourDropDownListName']").val();//第一層選擇的值
ShowSecondDropDownList(FirstLevel);
//為第一層下拉選單註冊onchange事件
$("select[name='YourDropDownListName']").change(function () {
var FirstLevel = $(this).val();//選擇的值
ShowSecondDropDownList(FirstLevel);
});
}
function ShowSecondDropDownList(FirstLevel)
{
$.ajax({
url: "@Url.Action("ShowSecondDropDownList","Home")",
type: "get",
data: { FirstLevel: FirstLevel },
async: false,
success: function (data) {
$("span").empty().html(data);
}
});
}
</script>
</head>
<body>
@*第一層下拉選單,因為範例關係,這頁沒有ViewModel,不然應該儘量用DropDownListFor()*@
@Html.DropDownList("YourDropDownListName", (IEnumerable<SelectListItem>)ViewData["FirstLevelItems"])
<span></span>
@*span裡要放第二層下拉選單的Html字串*@
</body>
</html>
圖解說明各流程
簡單明瞭~
連動下拉選單完成~
因為之前MSDN論壇有人問過↓類似問題,所以再把以下情況順便說明
如果Index.cshtml這個View剛好是編修畫面的View
狀況:假設會員甲在DB Table中儲存的選單資料是A、A3,然後進到編修畫面
以圖解說明,如何在Index.cshtml一載入畫面時,兩個下拉選單就已經預先選好A、A3
先看View(Index.cshtml)
再看Controller
注意上面兩張圖畫線畫框的地方即可完成
※我是覺得這樣就差不多,但執行後可以發現
就算使用者下拉第一層選單資料"A"時,第二層選單仍會停選在A3,也許有些人覺得正常應該要從”請選擇”開始選吧?
這其實有解法的,就交給看倌們自行發揮囉
結語
實務上,兩個下拉選單的值應該會用其他DB資料表來儲存
套用本範例時,請注意追加修改第二層下拉選單的ViewModel
在controller用LINQ撈出資料List,搭配View使@Html.DropDownListFor()即可達到不必寫for-loop也可以產生下拉選單
本文範例檔,放到MSDN訂閱期滿。