KO範例19 - 下拉選單連動效果
下拉選單間的連動在網頁設計十分普遍,例如: 主分類與次分類兩個下拉選單,選好主分類後,次分類的下拉選項要立即變成該主分類底下的次分類項目;另一個經典的例子則是---縣市鄉鎮與郵遞區號選取介面,幾乎是網頁開發的必修學分,網路上有不少範例及現成套件。
傳統做法就是在兩個下拉選單的onchange事件上做文章,說難不難,但寫起來挺囉索的。這回我們來看看用Knockout如何實現縣市鄉鎮區下拉選單連動。線上展示

開始之前,我們必須先取得縣市鄉鎮與郵遞區號資料來源,由於時間的關係,我已經預先烤好準備了一份(邊說邊從桌下端出半成品)台灣三碼郵遞區號的JSON檔,接著依照慣例,我們只需專心把ViewModel做好,其餘要跟DOM元素及事件打架的雞毛蒜皮瑣事就交給Knockout搞定!
我們在ViewModel中宣告5個屬性:
- cities
所有縣市名稱組成的observableArray,作為縣市下拉選單的options來源 - city
用以儲存所選取的縣市名稱,為縣市下拉選單的value繫結對象 - areas
為ko.computed,傳回city縣市所屬的鄉鎮市區資料物件陣列,作為鄉鎮市區下拉選單的options來源。這裡運用KO可以相依性追蹤特性,city一旦改變,areas就會立即重算,鄉鎮市區下拉選單的選項也馬上跟著變動,不知不覺間"連動"的功能就寫完了! - areaZip
儲存目前所選取的鄉鎮市區加郵遞區號資料,為鄉鎮市區下拉選單的value繫結對象 - addrPrefix
可寫入式ko.computed,將city與areaZip組成如"台北市大安區106"格式字串傳回,寫入資料時解析成縣市與鄉鎮市區郵遞區號兩部分,並同步至city及areaZip
以下是ViewModel的完整程式碼:
//cities為所有縣市名稱組成的observableArray
self.cities = ko.observableArray(cityNames);
self.city = ko.observable();
//areas為一computed,會傳回city縣市所屬鄉鎮市區資料物件陣列
self.areas = ko.computed(function () {
var areaData = taiwanZipData[self.city()];
for (var propName in areaData) {
value: propName + areaData[propName],
self.areaZip = ko.observable();
//用以傳回"台北市大安區106"格式之city + areaZip資料
//變更內容時,會將"台北市大安區106"格式解析並更新至city與areaZip
self.addrPrefix = ko.computed({
return (self.city() || "") + (self.areaZip() || "");
write: function (value) {
self.city(value.substr(0, 3));
self.areaZip(value.substr(3));
處理完ViewModel,網頁元素部分相對單純: 兩個下拉選單,value分別繫結到city及areaZip,options則繫結至cities及areas,另外再加一個input繫結到areaPrefix顯示輸入結果。
<select data-bind="options: cities, optionsCaption: '選擇縣市', value: city"></select>
<select data-bind="options: areas, optionsCaption: '選擇區域', optionsText: 'text', optionsValue: 'value', value: areaZip"></select>
<input data-bind="value: addrPrefix" />
就這樣,程式寫完了,真的!! 有趣的是,若在<input>中填入"台北市大安區106",下拉選單還會自動切到"台北市"及"大安區",代表修改文字內容或透過程式變更其值,下拉選單也會自動切換到對應位置,連<input>到<select>的反向連動也有了。
再一次證明"搞定ViewModel,介面就會自動自發地運作起來",這就是MVVM的魔力所在。(線上展示)
我不知道大家做何感覺,身為曾DIY打造過類似下拉選單的老鳥,在使用Knockout寫好範例的那一刻,我感動到想起立鼓掌! 啊~ 福氣啦~~~
[KO系列]
http://www.darkthread.net/kolab/labs/default.aspx?m=post