KO的反射

最近终于在实际项目中使用了 knockout.js,MVVM 模式的双向绑定和通知功能让我兴奋,感觉很不错,这个感觉就像当初从原生的 js 转向 jquery 一样,它开辟了一个新的编写模式,从更高的层次上满足了一些前端应用的需求。jquery 和 knockout 都是我觉得很 nice 的东西,对提高生产力有着很不错的促进作用,它们是前端完美的搭配。

jquery着重解决了同documet交互的问题,knockout着重解决了同business交互的问题

 

knockout.js 在文档方面写的还是比较详尽的,还有Live ExamplesTutorial,让你能很快的上手,不得不再称赞一下。它原生提供了很多的功能,可以满足绝大所数的开发需求了,唯独一点让我觉得有点遗憾,那就是 ko 的初始化,我最初的做法是先输出 json 给 ko 进行初始化,然后让 ko 更新到 UI,这样本身没什么问题,这也是教科书的做法。

 

但是倘若目标是一些零散的数据那就变得很繁琐了,我理想的方式还是直接把零散的数据绑定到UI上,然后让 UI 数据反射到 ko,这样就完美了,这样做还有一个好处:UI 可以及时显示该有的状态,因为通过 json 给 ko 初始化的方式总是会让我看到 ko 初始化后更新 UI 那一霎那的值变化,有点代码洁癖的我可受不了。

 

还好 ko 提供了自定义的扩展功能,使得应用变得无限的宽广了,我的想法也变得有思路来实现了,那就是做一个 reflect binding。

ko.bindingHandlers.reflect = {
    init: function (element, valueAccessor, allValueAccessor, viewModel) {
        // observable value
        var observableValue = valueAccessor(), allAccessor = allValueAccessor();
        if (observableValue === false) { return; }
        // reflect select
        if (element.tagName === 'SELECT') {
            var optionsAccessor = allAccessor['options'],
                txtKey = allAccessor['optionsText'],
                valKey = allAccessor['optionsValue'],
                caption = allAccessor['optionsCaption'];
            var existingOptions = optionsAccessor();
            if (!existingOptions || !existingOptions.length) {
                var options = [];
                if (caption) {
                    var c = {};
                    c[txtKey] = caption;
                    c[valKey] = '';
                    options.push(c);
                }
                $(element).find('option').each(function () {
                    var item = {};
                    item[txtKey] = $(this).text();
                    item[valKey] = $(this).val();
                    options.push(item);
                });
                optionsAccessor(options);
            }
            observableValue($(element).val());
        }
        // reflect textarea
        if (element.tagName === 'TEXTAREA') {
            observableValue($(element).val());
        }
        // reflect input
        if (element.tagName === 'INPUT') {
            var t = element.type.toLowerCase();
            if (t === 'text' || t === 'hidden') {
                observableValue($(element).val());
            }
            if (t === 'radio' || t === 'checkbox') {
                var target = element;
                if (element.name) {
                    var find = $('input[name="' + element.name + '"]:checked');
                    if (find.length > 0) { target = find.get(0); }
                }
                var elval = $(target).val();
                if (target.checked && elval) {
                    observableValue(elval);
                } else {
                    observableValue(target.checked);
                }
            }
        }
    }
};
<select data-bind="reflect:Passenger, value:Passenger, options:PassengerOptions, optionsText:'tint', optionsValue:'vint'">
    <option value="1">1 person</option>
    <option value="2">2 persons</option>
    <option value="3">3 persons</option>
</select>
<input type="text" data-bind="reflect:Destination, value: Destination" value="XMN" />
function modelClass() {
    this.Passenger = ko.observable();
    this.PassengerOptions = ko.observableArray([]);
    this.Destination = ko.observable();
}
ko.applyBindings(new modelClass());

 

使用时只需确保 reflect binding 放在 data-bind 第一位即可,这个 reflect binding 是对表单元素的反射绑定,可以按照这个原理在任意 html 元素上反射自己所需要的值。

 

posted on 2012-08-17 16:06  rulee  阅读(2264)  评论(2编辑  收藏  举报