接著我們來玩玩Knockout + Ajax,目標是做出如下介面,以關鍵字查詢顏色名稱:

這種需求很多老鳥用膝蓋都寫得出來,但Knockout的寫法肯定讓你耳目一新!

在ViewModel宣告一個ko.observableArray, results, 來放查詢結果,用<ul>以foreach繫結到results, 就能逐筆呈現結果。而results的內容怎麼來? 關鍵字查詢結果透過jQuery.getJSON()呼叫名為ColorData.ashx的ASP.NET程式傳回。有趣的是,我們不必攔截關鍵字<input>的onchange事件去觸發Ajax查詢,而是宣告一個匿名ko.computed,寫入:

ko.computed(function () {
    $.getJSON("ColorData.ashx", { k: self.keyword() },
        self.results);
});

全部的事情就會自己串起來,程式便寫完了!! 很神奇吧?

來解析一下運作原理: 由於ko.computed函數引用了self.keyword這個ko.observable,KO會記下這則相依性,一旦keyword內容變動,便會立即重算ko.computed觸發Ajax呼叫ColorData.ashx查詢,查詢結果會得到一個字串陣列,我們也不用多寫函數將它塞進results這個ko.observableArray,直接把results當成jQuery.ajax的succ函數,形同ko.observableArray(Ajax傳回的字串陣列),結果自動匯入,一桿進洞,很漂亮吧?

KO寫法比傳統攔截onchange自己處理Ajax查詢再塞回結果的做法高雅,且如要透過程式更動關鍵字欄位,只需變更ViewModel的ko.observable就好,該重算的該重查全都自動搞定。程式修改輸入欄位內容卻忘了觸發相關onchange事件是傳統的Bug大本營,在KO的運作模式下倒是灰飛煙滅。

程式碼如下: 線上展示

<!DOCTYPE html>
 
<html>
<head>
    <title>Lab 18 - 即時Ajax查詢</title>
    <script src="../Scripts/jquery-1.7.2.js"></script>
    <script src="../Scripts/knockout-2.1.0.debug.js"></script>
    <script>
        function MyViewModel() {
            var self = this;
            self.results = ko.observableArray();
            self.keyword = ko.observable("");
            //底下的computed函數引用了keyword(), 依KO會自動追查相依性的特色,
            //一旦keyword發生變動,這個函數就會被觸發執行
            //而$.getJSON()的第三個參數直接用observableArray去接收AJAX傳回資料陣列
            ko.computed(function () {
                $.getJSON("ColorData.ashx", { k: self.keyword() },
                    self.results);
            });
        }
 
        $(function () {
            ko.applyBindings(new MyViewModel());
        });
    </script>
</head>
<body>
<div>
    Keyword: <input data-bind="value: keyword" />
    <ul data-bind="foreach: results">
        <li><span data-bind="text: $data"></span></li>
    </ul>
</div>
</body>
</html>

以上的做法有個缺點,輸入文字後要按Tab鍵將焦點移開後才會觸發查詢,原因是預設<input>的value繫結是掛在onchange事件上,如果想做到按完鍵就更新,可改成: [參考]

<input data-bind="value: keyword, valueUpdate: 'afterkeydown'" /> (線上展示)

不過如同自動完成常面臨的課題,改為按鍵觸發在使用者輸入過程中會產生不必要的查詢,例如: 使用者想輸入"blue",如果每次按鍵就送一次查詢,共會丟出"b", "bl", "blu", "blue"四次查詢,但只有"blue"是有意義的。實務上的解決方法很簡單,每次按鍵後先按兵不動,等待一小段時間確定使用者未再輸入任何文字,才判定輸入完整送出查詢。Knockout有個throttle擴充方法,可以輕鬆實現延遲重算的模式,在這個案例中,ko.computed可改寫成:

ko.computed(function () {
    $.getJSON("ColorData.ashx", { k: self.keyword() },
        self.results);
}).extend({ throttle: 200 });

如此每次ko.computed被要求重算時,會先延遲200ms,確定沒有再次被呼叫才真的執行重算。若200ms內再次被觸發,則再從頭開始計算200ms延遲。線上展示

[KO系列]

http://www.darkthread.net/kolab/labs/default.aspx?m=post

Comments

# by Eric

請教黑大, 我有一個下拉選單options 跟 value 分別來自不同的ajax 的資料. 現在有一個問題是,因為ajax的request反應時間不一, 如果options 的來源速度較慢的話, 變成下拉選單的預設值就設定不到了. 不知道黑大這裡有沒有解法呢?

# by Jeffrey

to Eric, 如果是我,會考慮用$.when().done()等待兩個查詢都完成後再進行後續動作。另一種做法是利用$.ajax()的Deffered物件強制先查options再查value,但如此等於兩個Request不能同步執行,必須排隊,效率較差一些。

# by Eric

感謝黑大... 我試出來了 jquery 真是博大精深啊. 這讓我對黑大的景仰已經不是滔滔江水可以形容的了...

Post a comment