NG筆記29-下拉選單連動

跟同事討論到下拉選單連動(最常見的經典應用是縣市、行政區下拉選單連動,選取縣市後自動換成該縣市的行政區清單),這才發現針對這門必修課,我只寫過KO版範例,沒寫過NG版,趕緊補上。

我寫了一個三層式下拉選單連動範例,在ViewModel中安排Level1、Level2、Level3三個屬性保存下拉選單選取結果,另外用L1Options、L2Options、L3Options分別存放Level1-3的下拉選單選項。透過$scope.$watch(),在Level1變動時更新第二層選項,在Level1或Level2變動時更新第三層選項。更新選項時,若Level2/Level3的值不在選項中,則自動切到第一個選項。

為驗證反向操作,我還做了一個修改Level1、Level2、Level3值的按鈕,測試修改資料後下拉選單是否能正確對應。

<!DOCTYPE html>
<html ng-app="app">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>linked dropdowns</title>
  <style>
    body { font-size: 9pt; }
    div { padding: 6px; }
  </style>
</head>
<body ng-controller="main">
    <div>
        <select ng-model="m.Level1" ng-options="o as o for o in m.L1Options"></select>
        <select ng-model="m.Level2" ng-options="o as o for o in m.L2Options"></select>
        <select ng-model="m.Level3" ng-options="o as o for o in m.L3Options"></select>
    </div>
    <div>
    L1 = {{m.Level1}}, L2 = {{m.Level2}}, L3 = {{m.Level3}}
    </div>
    <div>
        <select ng-model="m.Path" ng-options="o as o for o in m.PathOptions"></select> 
        <button ng-click="m.SetLevels()">Set Levels</button>
    </div>
    <script src="https://code.jquery.com/jquery-3.0.0.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.min.js"></script>
    <script>
      function myViewModel(scope) {
        var self = this;
        self.Level1 = null;
        self.Level2 = null;
        self.Level3 = null;
        
        //模擬資料
        var data = self.Data = {
          "台北": {
            "文山": [ "政大" ],
            "大安": [ "台大", "台科大" ]
          },
          "新竹": {
            "東區": [ "交大", "清大" ]
          },
          "台南": {
            "東區": [ "成大" ],
            "官田": [ "南藝" ]
          }
        };
        
        //各Level對應的選項集合
        self.L1Options = Object.keys(self.Data);
        self.Level1 = self.L1Options[0];
        self.L2Options = [];
        self.L3Options = [];
        
        //Level1變更時連動L2Options
        scope.$watch("m.Level1", function() {
            self.L2Options = data[self.Level1] ? Object.keys(data[self.Level1]) : [];
            //檢查Level2是否在選項中,若無將Level2設定第一筆選項
            var idx = $.inArray(self.Level2, self.L2Options);
            if (idx == -1) self.Level2 = self.L2Options[0];
        });
        //Level1或Level2變更時連動L3Options
        scope.$watch("m.Level1+'/'+m.Level2", function() {
            self.L3Options = 
                data[self.Level1] && data[self.Level1][self.Level2] ?
                data[self.Level1][self.Level2] :
                [];
            //檢查Level3是否在選項中,若無將Level3設定第一筆選項
            var idx = $.inArray(self.Level3, self.L3Options);
            if (idx == -1 ) self.Level3 = self.L3Options[0];
        });
        
        //產生單層資料,形成下拉選單,用來測試更動Level1/Level2/Level3後連動是否正確
        var list = [];
        self.L1Options.forEach(function(city) {
            Object.keys(data[city]).forEach(function(area) {
                data[city][area].forEach(function(school) {
                    list.push(city + "/" + area + "/" + school);
                });
            });
        });
        self.Path = "";
        self.PathOptions = list;
        
        //按鈕後修改Level1/Level2/Level3
        self.SetLevels = function() {
            var p = self.Path.split('/');
            self.Level1 = p[0];
            self.Level2 = p[1];
            self.Level3 = p[2];
        };
        
      }      
      
      angular.module("app", [])
      .controller("main", function ($scope) {
        $scope.m = new myViewModel($scope);
      });
    </script>
</body>
</html>

在實務上,選項可能需要透過AJAX方式取回,此時將兩個$watch()函式改為AJAX查詢邏輯即可。JSBin上有Live Demo,大家可以動手玩玩。

[NG系列]

http://www.darkthread.net/kolab/labs/default.aspx?m=post&t=angularjs
歡迎推文分享:
Published 02 August 2016 07:59 AM 由 Jeffrey
Filed under:
Views: 13,317



意見

# 機密 said on 01 August, 2016 09:17 PM

如果是包在物件內,像是這樣的概念L1{L2[{L3[]}]}

那也可以用filter的方式進行過濾

缺點是第一次下來的清單會比較大

優點是寫的比較少XDDD

# Alison said on 03 August, 2016 03:44 AM

版主您好:  

冒昧打擾了~~我是松崗圖書的編輯黃小姐。在網路上看到您的網站上有許多分享技術的文章。不曉得您是否有想要撰寫書籍的想法呢?若您對於撰寫書籍有興趣,但是有其他比較偏好的主題,都歡迎分享看法。alisonhuang@kingsinfo.com.tw

希望能有機會合作書籍。

靜待佳音  

# Neal said on 30 August, 2016 11:55 AM

請問如果是四層連動, 模擬資料那邊, 格式要怎麼寫?

謝謝!

# Jeffrey said on 30 August, 2016 11:48 PM

to Neal, 差不多像這樣吧:gist.github.com/.../630f2a777a97748edff0b4412bae61ef

# Neal said on 31 August, 2016 08:19 AM

非常感謝您的回覆!

我用您的code來做這個功能, 但不知道問題出在哪?

jsbin.com/.../edit

麻煩您解惑了

# Jeffrey said on 31 August, 2016 10:28 AM

to Neal, Check it out! jsbin.com/.../edit

# Neal said on 31 August, 2016 11:27 AM

太感謝了!

# Neal said on 31 August, 2016 12:27 PM

不好意思, 我看了很久還是不知道, 我哪邊寫不對...

# Jeffrey said on 31 August, 2016 05:34 PM

to Neal, 不確定你是否有貼錯連結,你的版本只有第一個TD放了SELECT,後三個都是{{m.Level*}}。而資料連動的部分錯在self.L3Options = data[self.Level1] && data[self.Level1][self.Level2] ? Object.keys(data[self.Level1][self.Level2]) : []; 取第三層時仍要用Object.keys而不是直接當成陣列取用。

# Neal said on 31 August, 2016 11:50 PM

沒有貼錯連結, 其實我是想要做這種功能

jsbin.com/.../edit

以連結來說

第一欄位的

第一個對應西瓜

第二個對應火龍果

第三個對應蘋果

第二欄位可以隨著select改變, 把值show在第三第四第五欄位

只是現在變成全部都西瓜...

# Neal said on 01 September, 2016 02:32 AM

jsbin.com/.../edit

後來我改用別種方式

只是要預設顯示option第一個

這個就難倒我了

# Jeffrey said on 01 September, 2016 06:29 AM

to Neal, 若是第二欄位隨select改變, 值show在第三第四第五欄位,資料設三層就好,第三層放陣列。至於最後的寫法,預設option未選第一個因為存成物件屬性走(key,value)的限制多,我會改成物件陣列:jsbin.com/.../edit

# Neal said on 01 September, 2016 01:41 PM

請問像您這樣寫

jsbin.com/.../edit

要抓值的話, 是要怎麼設定值?

你的看法呢?

(必要的) 
(必要的) 
(選擇性的)
(必要的) 
(提醒: 因快取機制,您的留言幾分鐘後才會顯示在網站,請耐心稍候)

5 + 3 =

搜尋

Go

<August 2016>
SunMonTueWedThuFriSat
31123456
78910111213
14151617181920
21222324252627
28293031123
45678910
 
RSS
創用 CC 授權條款
【廣告】
twMVC

Tags 分類檢視
關於作者

一個醉心技術又酷愛分享的Coding魔人,十年的IT職場生涯,寫過系統、管過專案, 也帶過團隊,最後還是無怨無悔地選擇了技術鑽研這條路,近年來則以做一個"有為的中年人"自許。

文章典藏
其他功能

這個部落格


Syndication