專案需求一枚,用於匯率換算。來源幣別及目標幣別以下拉選單方式顯示,想當然爾,目標幣別跟來源幣別相同還轉換個屁,因此規格提到: 目標幣別的下拉選項應包含所有幣別,但排除來源幣別當下的選取幣別,特殊的下拉選單連動需求應運而生。
要用KO實現此一連動並不困難: 為兩個<SELECT>各宣告一個observableArray作為options繫結對象,也各宣告一個observable作為value的繫結對象以對應下拉選單選取值;接著宣告一個computed函式建立來源幣別選取值與目標幣別observableArray的關聯,一旦來源幣別變動,複製來源幣別的observableArray,剔除當下來源幣別選取值後將陣列設定成目標幣別observableArray的內容,大功告成。
進一步分析,前述的computed只有單一訂閱對象及單一更新對象,可再簡化,將目標幣別options直接繫結到一個computed(訂閱來源幣別選取值,傳回剔除選取值後的幣別陣列),便可以computed取代observableArray + computed。
最後提一下jQuery.map這個好東西,在JavaScript中處理陣列,許多初學者直覺的做法會宣告新的空陣列,跑迴圈逐一處理陣列元素再放入新陣列。例如: 需求為1-10的數字陣列只留單數並+10,變成11, 13, 15..,通常會寫成:
var orig = [1,2,3,4,5,6,7,8,9,10]; var res = []; for (var i = 0; i < orig.length; i++) { var v = orig[i]; if (v % 2 == 0) continue; res.push(v + 10); }
而jQuery.map可簡化上述程式,變成:
var orig = [1,2,3,4,5,6,7,8,9,10]; var res = $.map(orig, function(v, i) { if (v % 2 == 0) return null; //表示剔除 return v + 10; });
很精省吧?
完整程式碼如下: 線上展示
<!DOCTYPEhtml>
<html>
<head>
<metacharset=utf-8/>
<title>KO範例28 - 轉換對象(排除自己)之下拉選單連動</title>
</head>
<body>
<div>
From:
<selectdata-bind="options: ListA, value: PropA, optionsText: 't', optionsValue: 'v'"></select>
(<spandata-bind="text: PropA"></span>)
To:
<selectdata-bind="options: ListB, value: PropB, optionsText: 't', optionsValue: 'v'"></select>
(<spandata-bind="text: PropB"></span>)
</div>
<scriptsrc="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.9.1.js"></script>
<script src="http://ajax.aspnetcdn.com/ajax/knockout/knockout-2.2.1.js"></script>
<script>
function myViewModel() {
var self = this;
self.PropA = ko.observable();
self.ListA = ko.observableArray([
{ v:"TWD", t:"台幣" },
{ v:"USD", t:"美金" },
{ v:"EUR", t:"歐元" },
{ v:"JPY", t:"日幣" },
{ v:"HKD", t:"港幣" }
]);
self.PropB = ko.observable();
self.ListB = ko.computed(function() {
var a = self.PropA();
return $.map(self.ListA(), function(item) {
if (item.v == a) returnnull;
return item;
});
});
}
var vm = new myViewModel();
ko.applyBindings(vm);
</script>
</body>
</html>
[KO系列]