[ASP.net MVC 4] 使用AngularJS實現連動下拉選單(cascading dropdownlist)

[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>

執行結果:

第一次載入畫面時:

image

下拉選單變動

image

第二層下拉選單確實也跟著變動

image

 

本文範例檔

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>

 

 

參考文章:

Using AngularJS to consume WebAPI service in MVC4 project