[ASP.net MVC 4] 使用AngularJS實現連動下拉選單(cascading dropdownlist)
AngularJS在處理畫面上的資料連動,程式碼相當簡潔,玩了一下
很好奇連動下拉選單的話會怎麼做
以下端出試出來的結果,才剛入門不久, 或許有更好的寫法也說不定
順便紀錄幾個重點
1. AngularJS如何呼叫ASP.net MVC WebAPI
2. 連動下拉選單,新增資料時,下拉選單都選在”第一筆選項”,修改資料時,下拉選單要選在上次儲存的資料
昨晚寫到半夜3點半才弄好,現在頭很暈,無法Step by Step解說
直接看結果,說明在註解裡
WebApiConfig.cs
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
namespace MvcApplicationAngular
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "webapi/{controller}"
);
}
}
}
ValuesController.cs
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http;
using System.Web.Mvc;
namespace MvcApplicationAngular.Controllers
{
public class Option
{
//下拉選單的值
public string Value { get; set; }
//下拉選單的文字
public string Label { get; set; }
}
public class ValuesController : ApiController
{
// GET webapi/values
//第一層下拉選單資料
public IEnumerable<Option> Get()
{
IEnumerable<Option> options = new List<Option>()
{
new Option(){ Value="1", Label="第一個"},
new Option(){ Value="2", Label="第二個"}
};
return options;
}
// GET webapi/values?Value=2
public IEnumerable<Option> Get(string Value)
{
System.Threading.Thread.Sleep(1000); //模擬讀資料讀很久
//下拉選單第二層的資料
List<Option> options = new List<Option>()
{
new Option(){ Value="1-1", Label="第1-1個"},
new Option(){ Value="1-2", Label="第1-2個"},
new Option(){ Value="2-1", Label="第2-1個"},
new Option(){ Value="2-2", Label="第2-2個"},
};
//用Linq撈出特定的第二層資料
var result = from o in options
where o.Value.Substring(0, 1) == Value
select o;
return result;
}
}
}
HomeController.cs
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace MvcApplicationAngular.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
//假裝之前某使用者第一層下拉選單選擇2,第二層下拉選單選擇2-2
ViewData["level1SelectedValue"] = "2";//如果是新建資料畫面,就給1
ViewData["level2SelectedValue"] = "2-2";//如果是新建資料畫面,就給1-1
return View();
}
}
}
Index.cshtml
Layout = null;
}
<!DOCTYPE html>
<!--ng-app:整份html都是Angular的作用範圍-->
<html ng-app>
<head>
<meta name="viewport" content="width=device-width" />
<!--引用Angular核心函式庫-->
@Scripts.Render("~/Scripts/angular.js")
<!--引用jQuery核心函式庫-->
@Scripts.Render("~/Scripts/jquery-1.7.2.min.js")
<!--引用jQuery block ui為了實現讀取中效果-->
@Scripts.Render("~/Scripts/jquery.blockUI.min.js")
<script type="text/javascript">
<!--這個$scope感覺很像ViewModel..-->
function controller($scope, $http) {
//用Ajax撈Server端資料,初始化兩個下拉選單
$http.get("@Url.HttpRouteUrl("DefaultApi",new{controller="Values"})").success(function (data) {
$scope.level1 = data;
//產生第二層下拉選單
$http.get("@Url.HttpRouteUrl("DefaultApi",new{controller="Values",Value=ViewData["level1SelectedValue"].ToString()})").success(function (data2) {
$scope.level2 = data2;
}).error(function () {
$scope.error = "發生錯誤";
});
}).error(function () {
$scope.error = "發生錯誤";
});
//第一層下拉選單onchange
//selectedValue為選擇的Value字串
$scope.myChange = function (selectedValue) {
//隨便挑一個DOM來block UI,製造讀取中效果
$('body').block({ message: '<h1>Loading</h1>', css: { border: '3px solid #a00' } });
//產生第二層下拉選單
$http.get("@Url.HttpRouteUrl("DefaultApi",new{controller="Values"})"+"?Value="+selectedValue).success(function (data) {
//解除讀取中效果
$('body').unblock();
$scope.level2 = data;
}).error(function () {
//解除讀取中效果
$('body').unblock();
$scope.error = "發生錯誤";
});
}
}
</script>
</head>
<!--ng-controller:執行javascript的哪個controller function-->
<body ng-controller="controller">
<!--error變數($scope的成員)顯示錯誤訊息-->
<strong>{{ error }}</strong>
<!--ng-model:當作這個DOM的變數,要資料綁定的$scope的成員-->
<!--ng-init:該DOM的預設值,下拉選單的話,預設選定哪個Value-->
<!--ng-options:從level1集合陣列中,取出每個物件m,每個選項的值為m.Value,顯示文字為m.Label-->
<!--ng-change:下拉選單變動時,把選定的值(selectedValue字串)傳入myChange function-->
<select ng-model="selectedValue" ng-init="selectedValue='@ViewData["level1SelectedValue"]'" ng-options="m.Value as m.Label for m in level1" ng-change="myChange(selectedValue);" >
<option value="">-- 請選擇 --</option>
</select>
<br />
<!--ng-model不給會沒作用,所以隨便給個變數-->
<select ng-model="selectedValue2" ng-options="m.Value as m.Label for m in level2" ng-init="selectedValue2='@ViewData["level2SelectedValue"]'" >
<option value="">-- 請選擇 --</option>
</select>
</body>
</html>
執行結果:
第一次載入畫面時:
下拉選單變動
第二層下拉選單確實也跟著變動
本文範例檔
2013.6.2追記:Global.asax.cs在Application_Start()我已經加了
GlobalConfiguration.Configuration.Formatters.XmlFormatter.SupportedMediaTypes.Clear();
所以WebAPI都是回傳JSON格式
如果要指定下拉選單預設選擇的值,除了ng-init,在controller裡也可以設定$scope的成員
如下:
Layout = null;
}
<!DOCTYPE html>
<html ng-app>
<head>
<meta name="viewport" content="width=device-width" />
@Scripts.Render("~/Scripts/angular.js")
@Scripts.Render("~/Scripts/jquery-1.7.2.min.js")
@Scripts.Render("~/Scripts/jquery.blockUI.min.js")
<script type="text/javascript">
function controller($scope, $http) {
//下拉選單一開始選定的值
$scope.selectedValue = "@ViewData["level1SelectedValue"]";
$scope.selectedValue2 = "@ViewData["level2SelectedValue"]";
$http.get("@Url.HttpRouteUrl("DefaultApi",new{controller="Values"})").success(function (data) {
$scope.level1 = data;
$http.get("@Url.HttpRouteUrl("DefaultApi",new{controller="Values",Value=ViewData["level1SelectedValue"].ToString()})").success(function (data2) {
$scope.level2 = data2;
}).error(function () {
$scope.error = "發生錯誤";
});
}).error(function () {
$scope.error = "發生錯誤";
});
$scope.myChange = function (selectedValue) {
$('body').block({ message: '<h1>Loading</h1>', css: { border: '3px solid #a00' } });
$http.get("@Url.HttpRouteUrl("DefaultApi",new{controller="Values"})"+"?Value="+selectedValue).success(function (data) {
$('body').unblock();
$scope.level2 = data;
}).error(function () {
$('body').unblock();
$scope.error = "發生錯誤";
});
}
}
</script>
</head>
<body ng-controller="controller">
<strong>{{ error }}</strong>
<select ng-model="selectedValue" ng-options="m.Value as m.Label for m in level1" ng-change="myChange(selectedValue);" >
<option value="">-- 請選擇 --</option>
</select>
<br />
<select ng-model="selectedValue2" ng-options="m.Value as m.Label for m in level2" >
<option value="">-- 請選擇 --</option>
</select>
</body>
</html>
參考文章: