雖然knockout.js已經提供很多好用的繫結,例如: value、text、checked、attr、css... 等等,但實務上一定有無法滿足需求的情境。針對這點,KO允許開發者自訂繫結行為,以滿足專案中稀奇古怪的需求。這已屬進階型應用,但深入了解後就會發現此一擴充特性,讓KO更顯威力強大!!

舉一個常見但重要的需求: 到目前為止,我們繫結的輸入欄位都是Select、TextBox、CheckBox等標準HTML元素,但實務上這類瀏覽器標準操作元件常被使用者嫌棄,嫌土嫌無趣,通常得改用jQuery UIKendoUI之類UI套件才能上得了檯面。範例12便將展示如何用knockout.js整合KendoUI的日期選擇器

自訂繫結時,基本寫法是在ko.bindingHandlers加上自訂物件,並實做init及update兩個方法:

ko.bindingHandlers.myBinding = {
    init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
        // 第一次繫結時觸發, 主要用來設定初值、為元素加上外掛、掛載事件... 等
        //element指向對象元素
        //透過valueAccess()可取得繫結標的
        //透過allBindingsAccessor可取到同一data-bind中其他設定

    },
    update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
        // 第一次繫結及之後每次繫結對象值改變時觸發,主要用來更新元素反應資料改變
    }
}

以KendoUI的日期選擇器為例,在init階段的要務是透過element參數取得<input type="text">元素,呼叫valueAccessor()取得ko.observable物件,接著以KendoUI的.kendoDataPicker()方法將TextBox升級成日期選擇器,並在日期選擇器change事件中,將修改後的新值寫回ko.observable。為了示範data-bind可一併傳入其他參數(就像下拉選單的options還可以搭配optionsText/optionsValue一樣),範例再多增加一個kendoDateFormat參數,可用以指定日期選擇器的日期格式。

update會發生在ViewModel屬性改變時,所以我們只需取得屬性新值,透過KendoUI提供的方法將其指定給日期選擇器就大功告。

完整程碼如下: 線上展示

<!DOCTYPE html>
 
<html>
<head>
    <title>Lab 12 - 自訂Binding</title>
    <script src="../Scripts/jquery-1.7.2.js"></script>
    <script src="../Scripts/knockout-2.1.0.debug.js"></script>
    <script src="../Scripts/kendo/kendo.web.min.js" type="text/javascript"></script>
    <link href="../Content/kendo/kendo.common.min.css" rel="stylesheet" type="text/css" />
    <link href="../Content/kendo/kendo.metro.min.css" rel="stylesheet" type="text/css" />
    <script>
        ko.bindingHandlers.kendoDate = {
            init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
                // 第一次繫結時觸發, 主要用來設定初值、為元素加上外掛、掛載事件... 等
 
                //element指向對象元素
                var $elem = $(element);
                //透過valueAccess()可取得繫結標的
                var val = valueAccessor();
                //透過allBindingsAccessor可取到同一data-bind中其他設定
                var allBindParams = allBindingsAccessor();
 
                //轉換成日期選擇器
                $elem.kendoDatePicker({
                    //用unwrapObservable的好處不管observable或一般屬性,均能順利取值
                    value: ko.utils.unwrapObservable(val),
                    //取得日期格式, 使用||技巧補上未指定時的預設值
                    format: allBindParams.kendoDateFormat || "yyyy/MM/dd",
                    //使用者選取日期後更新屬性
                    change: function (e) {
                        var dp = $elem.data("kendoDatePicker");
                        //將值設定回ViewModel屬性
                        if (ko.isObservable(val))
                            val(dp._value);
                    }
                });
 
            },
            update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
                // 第一次繫結及之後每次繫結對象值改變時觸發,主要用來更新元素反應資料改變
                //將ViewModel的屬性值設定到Kendo UI DatePicker上
                var kdd = $(element).data("kendoDatePicker");
                kdd.value(ko.utils.unwrapObservable(valueAccessor()));
            }
        }
 
        function MyViewModel() {
            var self = this;
            self.regDate = ko.observable(new Date(2012, 11, 21));
            self.regDateDisp = ko.computed(function () {
                return kendo.toString(self.regDate(), "yyyy/MM/dd");
            });
            self.setToday = function () { self.regDate(new Date()) };
        }
 
        $(function () {
            ko.applyBindings(new MyViewModel());
        });
    </script>
</head>
<body>
<input type="button" value="Set Today" data-bind="click: setToday"/>
<br />
<input type="text" data-bind="kendoDate: regDate" />
<input type="text" data-bind="kendoDate: regDate, kendoDateFormat: 'yyyy-MM-dd'" />
 
</body>
</html>

操作時,由於左右兩個欄位都繫結到同一個ViewModel屬性,改變左邊欄位會一併會更動右邊欄位值,而右邊透過kendoDateFormat參數額外指定yyyy-MM-dd日期格式,故同一日期值在左右兩欄有不同的顯示結果。最上方的Set Today鈕,按下後會將ViewModel屬性設為當天日期,用以驗證ViewModel到元素方向的繫結運作正確。

已經捲起袖子準備為每個KendoUI元件寫一個自訂繫結了嗎? 且慢! 報告大家好消息,網路上已有佛心的開發寫妥Knockout-Kendo.js了,抱著感恩的心情享用吧!

[KO系列]
http://www.darkthread.net/kolab/labs/default.aspx?m=post

Comments

Be the first to post a comment

Post a comment